Skip to content

Issue Examples

A minimal reproduction + outcome + fix for each entry in Known Issues, so every one is concrete. Numbers match that page.


#1 — Odd downsample_factor → 0-byte mp4

use_fast_output(engine="workbench", downsample_factor=6)   # 1280 // 6 = 213 (odd width)
with bare_scene as shot:
    shot.add(brian("A")); shot.camera("static"); shot.clip(1/24)
What happens: every frame renders, then the libx264/yuv420p encode aborts — "Error while opening encoder — incorrect parameters such as … width or height" — leaving a 0-byte .mp4 (libx264 needs even dimensions). Fix / workaround: use a factor that divides evenly — 1, 2, 4, 5, 8. (Code fix: add -vf "scale=ceil(iw/2)*2:ceil(ih/2)*2" to the proxy ffmpeg call.)

#2 — Edit output root is a hardcoded absolute path

edit = Edit("my_cut")          # → writes under a hardcoded /Users/.../edits path
What happens: breaks if the repo moves or runs elsewhere. Workaround: pass it explicitly — Edit("my_cut", output_root="…/edits").

#3 — pan / jitter / zoom not in the public camera API

shot.camera("pan")        # ValueError: Unknown camera type: 'pan'
What happens: these exist in dsl/camera.py but aren't wired into shot.camera()'s dispatch — only reachable via the low-level comparison scripts (render_camera_compare*.py).

#4 — arc accepts only 90° or 180°

shot.camera("arc", arc=120)     # ValueError: arc must be 90 or 180
Fix / workaround: use arc=90 or arc=180 (the math would interpolate any angle; the guard is artificial).

#5 — say() clip length follows the animation, not the speech

hero.say("Hi.")        # ~1 s of speech …
# … but the shot is ~6.5 s — the length of the 'talking' animation
What happens: short lines still produce long clips. Workaround: shot.clip(2.0) to trim, or say(line, action_name="idle") for a shorter hold.

#6 — move_to(action="running") progressively collapses

hero.move_to(dest, action="running")
What happens: the pose degrades over the clip — lean → crouch → near-horizontal → collapsed on the ground (a longer clip is worse). Only running is affected.

A frame (left) and the full 8-second run (right) — it degrades and never recovers:

running collapses (frame)

Workaround: for run-travel use jogging / sprint / fast_run (all fine).

#7 — perform(duration=N) ignored for in-place actions

hero.actor.perform("idle", duration=20)   # ask for 20 frames …
# … frame_end is still ~121 (the full native idle plays)
What happens: the strip's length is reset to the action's native length, so duration is discarded (in-place actions only — locomotion length comes from travel distance). Workaround: bound the shot with shot.clip(seconds).

#8 — edit.music() BGM comes out near-inaudible

edit.music("bgm/atmospheric_pad", volume=0.15)   # measured ~ -63 dB = silent
What happens: add_bgm mixes with amix, which halves each input, so a low volume BGM vanishes (quiet ambient tracks especially). Workaround: raise volume to compensate — 0.60.8 for present tracks, higher for quiet ambient ones. (Code fix: amix=…:normalize=0.)


Generation & realism limitations

A different class from the bugs above — quality and reasoning gaps seen across full generated movies (the Known Issues table, #12–#19). These mostly have no one-line fix; each is shown here on a real rendered shot. Numbers match the Known Issues table.

#12 — TTS voice doesn't match the character

A character's voice comes from its rig entry in assets/character_descriptions.json. Cast a rig whose voice is the wrong gender/tone for the role and the spoken line doesn't match the character on screen. The frame, and the clip (▶ play with sound):

character on screen

Workaround (historical): cast a rig whose voice fit the role, or edit the voice field.

✅ Resolved — voice split from the rig

The voice/rig split (dsl/voices.py + the new gender/default_voice schema) fixes this: every character carries a gender, every voice is gender-tagged, and all 36 pairings are now gender-matched. james (Marcus's rig) was repinned marin → echo, so the clip above is the "before". Cast voices explicitly — define once at the top, then reuse in every shot:

# ── top of the film / scene module: import + cast once ─────────────
from dsl.actors import james, megan
from dsl.voices import echo, nova            # gender-matched voices

marcus = james("Marcus", voice=echo)         # male rig + male voice
nina   = megan("Nina",  voice=nova)          # female rig + female voice

# ── then just use them in any shot ─────────────────────────────────
with BRIDGE.Canal_Bridge2_Middle.E2 as s:
    s.block(face_to_face(), nina, marcus)
    marcus.say("You get on that plane knowing this.", action_name="talking")

Or override inline for a one-off (voice by singleton or string name, plus a delivery note):

villain = brian("The Stranger", voice="onyx",
                instructions="Cold, clipped, military. No warmth.")

#13 — actors clip through set geometry

There is no collision between actors and the set. A blocking offset or walk path can drive a character straight through a post, fence, or prop. Two instances:

Bridge — Sam walks through the wooden post :

Sam clipping through the pergola

Rooftop — the approach path crosses the wall:

approach crossing the rooftop blocks

Workaround: none in-engine. Place anchors/blocking with clearance from geometry, and frame around contact points.

#14 — animations read as awkward

On most rigs the in-place gesture actions look stiff or subtly wrong — by observation 80–90% of them feel "off". Locomotion is the most reliable; gestures are the weakest. Two examples:

sizeup — the pose hunches awkwardly:

awkward sizeup pose

considers — the gesture snaps stiffly:

stiff considers gesture

Mitigation: it's a retarget/asset-quality limit, not a code bug — favour the cleaner-reading actions, keep gesture holds short, and let camera + edit sell the beat.

#15 — blocking is flat and not varied

The blocking primitives are purely geometric, so staging often reads as unrealistic. Two examples:

Crowd — figures line up at one depth and overlap into each other:

crowd crammed in a flat line

Two-hander — the pair is awkardly faced for the realistic scenarios:

two-hander staged too far apart

Mitigation: Can have some more realistion blockin and positioning

#16 — camera & subject get obstructed

The camera solve doesn't test line-of-sight, so set geometry can land between the lens and the subject — sometimes hiding the actor entirely. Two instances:

Fence — the confession push-in sits inside the fence posts, fully blocking Marcus (even though the shot asked for his "clear, unobstructed angle"):

camera buried in the fence posts

Wall — a wall drops in front of the lens and hides the standing actor:

wall hides the subject

Mitigation: choose camera points/angles with a clear sightline to the subject; nudge theta/phi/R until the view is unobstructed.

#17 — weak spatial reasoning

Understanding statement. Occlusion and 3-D space are genuinely hard to reason about from a script alone — even a human director can't reliably predict what the lens will see without looking. The generator inherits this: it can't "see" the set, so obstruction (#16), clipping (#13) and awkward staging (#15) all trace back here. The practical fix is iterative — render, look, nudge.

#18 — sparse anchors confuse relative positioning

Understanding statement. A set exposes only a handful of named anchor points, so when a shot needs a position relative to another actor (trailing/following, "just behind", "off to the side"), there are too few hooks and the placement gets imprecise. Denser anchors — or explicit relative offsets — reduce the guesswork.

#19 — gesture animations don't auto-loop to fill a duration

Understanding statement. Repeating an animation = call it again (for perform/act); and for say() you can't loop the gesture — you split the line so each chunk re-triggers it. Locomotion anims are the only ones that auto-repeat (to fill a to=/duration); gestures just hold their last frame.

# from movie_samples/home_game/s24.py — a ~20 s confession.
# Split the speech into sentences so the "talking" gesture RE-TRIGGERS on each;
# one long say() would play the gesture ONCE and then freeze for the rest.
marcus.say("You were nine when I became your whole world, and I had no clue what I was doing.", action_name="talking")
marcus.say("I just never wanted you to see me scared, so I never let you see me anything.",     action_name="talking")
marcus.say("But you get on that plane tomorrow knowing this: every early practice, every night I waited up.", action_name="talking")
marcus.say("I'd do all of it again. You're the best thing I ever made, Dev.", action_name="talking")