Skip to main content

Overview

AI Studio’s video creation feature transforms static property photos into dynamic walkthrough videos. Using AI-powered motion generation and intelligent transitions, you can create professional property tours in minutes.

Video Creation Workflow

The video wizard guides you through a step-by-step process:
1

Choose Creation Mode

Select how you want to build your video:

Template Mode

Start with a pre-configured storyboard template:
  • Predefined room sequence
  • Professional shot ordering
  • Optimal transition timing

Custom Mode

Build from scratch:
  • Select your own images
  • Assign room types manually
  • Full control over sequence
// Source: components/video/video-creation/video-creation-wizard.tsx
const TEMPLATE_STEPS = [
  "select-template",
  "storyboard",
  "select-music",
  "review"
];

const CUSTOM_STEPS = [
  "select-template",
  "select-images",
  "assign-rooms",
  "select-music",
  "review"
];
2

Select Images

Choose images from your completed photo editing projects or upload new ones.Image Requirements:
  • Minimum 3 images
  • Maximum 12 images per video
  • All images must be from the same workspace
  • Can mix enhanced and original images
// Source: lib/db/schema.ts
export const videoClip = pgTable("video_clip", {
  id: text("id").primaryKey(),
  sourceImageUrl: text("source_image_url").notNull(),
  imageGenerationId: text("image_generation_id"), // Link to edited photo
  endImageUrl: text("end_image_url"), // Optional end frame for smooth transitions
  roomType: text("room_type").notNull(),
  sequenceOrder: integer("sequence_order").notNull(),
});
3

Assign Room Types & Sequence

Label each image and arrange them in the desired order:
Each image needs a room type for proper sequencing:
  • Living Room
  • Kitchen
  • Bedroom
  • Bathroom
  • Exterior
  • Garden
  • And more…
Room types help the AI understand context for motion generation.
4

Configure Music & Settings

Customize your video’s audio and format:
Choose from royalty-free music tracks:
// Source: lib/db/schema.ts
export const musicTrack = pgTable("music_track", {
  id: text("id").primaryKey(),
  name: text("name").notNull(),
  artist: text("artist"),
  category: text("category").notNull(), // modern | classical | upbeat | calm
  audioUrl: text("audio_url").notNull(),
  durationSeconds: integer("duration_seconds").notNull(),
  bpm: integer("bpm"), // For tempo matching
});
Categories:
  • Modern: Contemporary electronic and pop
  • Classical: Orchestral and piano
  • Upbeat: Energetic and lively
  • Calm: Relaxing and ambient
  • Cinematic: Epic and dramatic
5

Review & Generate

Final review before generation:
  • Project name
  • Image count and sequence
  • Music selection
  • Estimated cost
  • Total duration (~5 seconds per clip)
Click Generate Video to start the AI video generation process.

AI Video Generation Pipeline

When you generate a video, AI Studio orchestrates a complex multi-stage pipeline:

Pipeline Architecture

// Source: trigger/video-orchestrator.ts
export const generateVideoTask = task({
  id: "generate-video",
  queue: {
    name: "video-generation",
    concurrencyLimit: 1, // Process one video at a time
  },
  maxDuration: 1800, // 30 minutes total
});

Stage 1: Clip Generation

All clips are generated in parallel for speed:
// Source: trigger/video-orchestrator.ts
const clipResults = await generateVideoClipTask.batchTriggerAndWait(
  clips.map((clip) => ({
    payload: {
      clipId: clip.id,
      tailImageUrl: clip.endImageUrl || clip.sourceImageUrl,
      targetRoomLabel: clip.roomLabel || clip.roomType,
    },
  }))
);
Each clip is processed independently:
  • Image-to-video AI generation
  • Motion synthesis
  • 5-second duration per clip

Stage 2: Transition Generation

Seamless transitions create smooth morph effects between clips.
// Source: trigger/video-orchestrator.ts
const clipsWithTransitions = clips.filter((clip, index) => {
  return clip.transitionType === "seamless" && index < clips.length - 1;
});

if (clipsWithTransitions.length > 0) {
  const transitionResults = await generateTransitionClipTask.batchTriggerAndWait(
    clipsWithTransitions.map((clip) => {
      const nextClip = clips[clipIndex + 1];
      return {
        payload: {
          clipId: clip.id,
          fromImageUrl: clip.endImageUrl || clip.sourceImageUrl,
          toImageUrl: nextClip.sourceImageUrl,
          aspectRatio: videoProject.aspectRatio,
        },
      };
    })
  );
}
Transition Types:

Seamless

AI-generated morph between clips:
  • Smooth visual blending
  • 1-2 second duration
  • Natural scene transitions

Cut

Direct cut between clips:
  • Instant transition
  • No processing overhead
  • Traditional editing style

Stage 3: Video Compilation

Once all clips and transitions are ready, the final video is compiled:
// Source: trigger/compile-video.ts
metadata.set("status", {
  step: "compiling",
  label: "Compiling video…",
  progress: 70,
});

// Trigger and wait for compilation
const compileResult = await compileVideoTask.triggerAndWait({
  videoProjectId,
});
Compilation Process:
  1. Sequence Assembly: Clips ordered by sequenceOrder
  2. Transition Insertion: Seamless clips inserted between main clips
  3. Audio Mixing: Background music + native audio (if enabled)
  4. Format Export: Final render in selected aspect ratio
  5. Thumbnail Generation: Extract frame for video preview
// Source: lib/db/schema.ts
export const videoProject = pgTable("video_project", {
  id: text("id").primaryKey(),
  finalVideoUrl: text("final_video_url"),
  thumbnailUrl: text("thumbnail_url"),
  durationSeconds: integer("duration_seconds"),
  status: text("status").notNull().default("draft"),
});

Video Project Status Flow

Video projects progress through distinct stages:
export type VideoProjectStatus =
  | "draft"      // Initial creation, not yet started
  | "generating" // AI clip generation in progress
  | "compiling"  // Assembling final video
  | "completed"  // Ready to view/download
  | "failed";    // Generation failed
1

Draft

Video project created but generation not started.Actions:
  • Edit settings
  • Modify clip sequence
  • Start generation
2

Generating

AI is creating motion clips for each image.Progress Indicators:
// Source: lib/db/schema.ts
clipCount: integer("clip_count").notNull().default(0),
completedClipCount: integer("completed_clip_count").notNull().default(0),
Typical duration: 3-8 minutes depending on clip count.
3

Compiling

Clips are being assembled into final video.Activities:
  • Stitching clips together
  • Adding transitions
  • Mixing audio tracks
  • Encoding final output
Duration: 1-3 minutes.
4

Completed

Video is ready!Available Actions:
  • Preview video
  • Download (MP4)
  • Share link
  • View analytics
5

Failed

Something went wrong during generation.Error Handling:
errorMessage: text("error_message"),
Check error message and contact support if needed.

Cost Estimation

Video generation costs are calculated based on:
// Source: lib/video/video-constants.ts
export function calculateVideoCost(
  clipCount: number,
  clipDuration: number,
  generateNativeAudio: boolean
): number {
  const baseClipCost = 0.35; // $0.35 per 5-second clip
  const nativeAudioCost = generateNativeAudio ? 0.10 : 0;
  
  return (baseClipCost + nativeAudioCost) * clipCount;
}
Example Costs:
  • 5 clips (25s video): 1.751.75 - 2.25
  • 8 clips (40s video): 2.802.80 - 3.60
  • 12 clips (60s video): 4.204.20 - 5.40
Costs are estimated during the review step and finalized after successful generation.

Real-Time Progress Updates

The video detail page shows live progress using Trigger.dev’s real-time capabilities:
// Source: components/video/video-detail-content.tsx
const { run } = useRealtimeRun(videoProject.triggerRunId, {
  accessToken: videoProject.triggerAccessToken,
  enabled: !!videoProject.triggerRunId,
});

const status = run?.metadata?.status as GenerateVideoStatus;

// Display progress
<div>
  <p>{status.label}</p>
  {status.clipIndex && (
    <p>Clip {status.clipIndex + 1} of {status.totalClips}</p>
  )}
  <ProgressBar value={status.progress} />
</div>
Progress Data:
export interface GenerateVideoStatus {
  step: "starting" | "generating" | "compiling" | "completed" | "failed";
  label: string;
  clipIndex?: number;
  totalClips?: number;
  progress?: number;
}

Viewing & Downloading

Video Player

Once completed, videos can be played directly in the browser:
<video
  controls
  className="aspect-video w-full rounded-lg"
  src={videoProject.finalVideoUrl}
  poster={videoProject.thumbnailUrl}
>
  Your browser does not support video playback.
</video>
Player Features:
  • Play/pause controls
  • Volume adjustment
  • Fullscreen mode
  • Seek bar
  • Thumbnail preview

Download Options

Click the Download button to save the MP4 file:
const downloadVideo = async () => {
  const response = await fetch(videoProject.finalVideoUrl);
  const blob = await response.blob();
  const url = window.URL.createObjectURL(blob);
  const a = document.createElement("a");
  a.href = url;
  a.download = `${videoProject.name}.mp4`;
  a.click();
};

Advanced Features

Custom Motion Prompts

For advanced users, you can customize the motion prompt for specific clips:
// Source: lib/db/schema.ts
motionPrompt: text("motion_prompt"), // Custom motion description
Example prompts:
  • “Slow zoom into the center of the room”
  • “Pan left to right showing the kitchen layout”
  • “Forward dolly movement through the hallway”
  • “Gentle tilt up to reveal the ceiling details”

Multi-Image Transitions

Create dynamic transitions by specifying different start and end images:
// Source: lib/db/schema.ts
sourceImageUrl: text("source_image_url").notNull(), // Starting frame
endImageUrl: text("end_image_url"), // Ending frame (optional)
Use cases:
  • Before/after transformations
  • Room transitions (day to night)
  • Seasonal changes (summer to winter exterior)

Best Practices

Choose images strategically:
  • Variety: Mix wide shots with detail shots
  • Lighting: Consistent lighting across all images
  • Quality: High-resolution images (minimum 1920x1080)
  • Composition: Follow rule of thirds for visual interest
  • Story Flow: Logical progression through the property
Order clips to tell a compelling story:
  1. Hook: Start with the most impressive shot (exterior or best room)
  2. Flow: Move logically through spaces (don’t jump randomly)
  3. Pace: Mix room types to maintain interest
  4. Climax: Save a stunning view or unique feature for near the end
  5. Close: End with exterior or aerial shot
Match music to property type:
  • Luxury Properties: Cinematic or classical
  • Modern Apartments: Upbeat or modern
  • Family Homes: Calm or warm tones
  • Commercial: Professional and energetic
Volume guidelines:
  • 40-50%: Background ambiance
  • 60-70%: Prominent soundtrack
  • 30-40%: When using native audio
Use Seamless Transitions:
  • Between similar room types
  • For smooth storytelling
  • When showing progression
Use Cut Transitions:
  • Between very different spaces
  • For faster pacing
  • To reduce processing time

Troubleshooting

If generation exceeds expected time:
  1. Check Progress: Look at clip completion count
  2. Wait for Timeout: System will auto-retry failed clips
  3. Partial Success: If some clips complete, you can still get a video
// Source: trigger/video-orchestrator.ts
// If some clips failed, log warning but continue
if (failedClips.length > 0) {
  logger.warn("Some clips failed to generate", {
    failedCount: failedClips.length,
  });
}
If clip motion looks unnatural:
  • Image Quality: Ensure source images are high resolution
  • Motion Prompt: Try custom motion prompts for better control
  • Room Type: Verify correct room types are assigned
  • Retry: Delete and regenerate the specific clip
If music doesn’t sync properly:
  • Music is looped to match video duration automatically
  • Check music volume settings (may be too low)
  • Disable native audio if it conflicts
  • Try a different music track

Technical Specifications

Output Specifications:
  • Format: MP4 (H.264)
  • Frame Rate: 30 fps
  • Bitrate: 5-8 Mbps
  • Audio: AAC, 192 kbps
Aspect Ratios:
  • 16:9: 1920x1080
  • 9:16: 1080x1920
  • 1:1: 1080x1080

Build docs developers (and LLMs) love