Files
hypit/docs/quickstart/composition.md
T
rponeawa e2e64d8a8e fix: remove the friction a reconstruction run actually paid for (#63)
A full reconstruction reported fourteen places the route cost it time. Twelve held. What is here is
the subset that changes the next run rather than the last one, and most of it is code: a fact stated
in a document is a fact every future reader has to carry, so where the tool could stop being wrong
instead, it does.

**The cut re-anchors instead of dropping.** An element given the span it occupies and a Moment to
close it — `during="program" until={story.moment.done}` — is on screen for the whole of any stretch
that ends before that word. `render_element` dropped it, so the one element a reference is about was
unrenderable over every Segment but the last, while the loop asks for it to be compared over each of
them. A close that has not arrived is now removed from the tag and the element stays. Marked before
the cut it really is gone, and marked inside it survived already. Measured on the run that found it:
one Segment renderable before, four after, and the two that still refuse now name a Producer failure
the drop was hiding.

**The package root comes from the Run.** It was the working directory, so naming a Run by path
resolved its imports somewhere else and the project's own elements read as packages that do not
exist. It is now the nearest `package.json` at or above the Run — the same walk `hypit check` makes.
`--package-root` still overrides it and is no longer needed to check a project from anywhere.

**The mock's frame is a hairline.** At a twentieth of the shorter side it was fifty pixels thick on a
full-frame mock, and an observer measuring what an element covers read that as the element stopping
short of the edge — reported every round, about the mock rather than about the Source.

**The comparison names its own pair.** The prompt asks for each difference to say which of the two it
is in, and `observers.md` says which is which: one is the reference, two is the render. The observer
is not told, and the caller was not either.

**`--reference-id` is in the command block** it was missing from, with what it decides. Six of the
run's first comparisons were refused for a mismatch this flag exists to prevent, and it was found by
grepping the tool's source.

Corrections that cost no length: `vocabulary.md` had the inspect output's shape wrong — Recipe
properties are inside the `recipe` attribute, not beside `attributes` — which is why the run read the
file's central claim as false. `styles.md` and `composition.md` documented two Cue keys the Style
refuses and a `display=` that is `document=`.

Two facts moved to where the reader already is rather than into the skill: `media-track`'s `extent`
summary says the ratio is what it decides, and `VisualElement` says position is measured from the
parent. The other runtime-only errors already name their own fix.
2026-08-25 02:01:28 +08:00

10 KiB

title, description
title description
Film & Rendering Composing tracks into a film and rendering to video.

Film & Rendering

Film is the final assembly stage. It takes all peer Tracks, validates them, and produces a Composition. The renderer then compiles that Composition into an MP4 video.

<import as="space" from="@hypit/spatial@1"/>
<import as="film" from="@hypit/film@1"/>
<import as="render" from="@hypit/render-hyperframes@1"/>

film:Film

Assembles all Tracks into a single Composition. Film itself has no domain knowledge — it does not know what a caption is, what Media is, or what speech is. It takes any VisualTrack or AudioTrack and layers them by stacking order.

<space:Canvas id="vertical" width="1080" height="1920"/>
<film:Film id="main" canvas={vertical} semantic={speech.semantic} appearance={recipes.film.vertical}>
  <film:Track source={speech.visual}/>
  <film:Track source={speech.audio}/>
  <film:Track source={captions.track}/>
  <film:Track source={product-broll.visual}/>
  <film:Track source={titles.track}/>
</film:Film>
Attribute Required Description
id yes Unique identifier
canvas yes Explicit CanvasSpace shared with Track layout
semantic yes SemanticTrack from speech:Track — defines duration and frame rate
appearance yes SVS Film Recipe — the canvas clear color

film:Track

Each <film:Track> child adds a Track source to the composition:

Attribute Required Description
source yes A VisualTrack or AudioTrack from any upstream component

Common Track sources:

Source Type From
{speech.visual} VisualTrack speech:Track — sparse same-source speech visuals
{speech.audio} AudioTrack speech:Track — synchronized audio
{captions.track} VisualTrack a Caption Style-family Track — timed captions
{cards.visual} VisualTrack media-track:Track — media overlays or B-roll
{titles.track} VisualTrack text:Track — text overlays

Track stacking

Tracks are flat — there is no nesting or grouping. Z-ordering is determined entirely by the stack-order property in each Track's SVS Recipe. Lower values go behind; higher values render on top.

Typical stacking order:

stack-order Content
10 Example Speech visual (author-selected, not a built-in default)
40 Media overlays
70 Captions
90 Text overlays

One component can emit multiple visual elements (Presents) at different z-positions, which interleave with other components' Presents. The final render flattens all Presents, sorts by absolute stacking key, and paints them onto one canvas.

Output: {main.composition} — the complete Composition, passed to the renderer.

render:Video

Compiles the Composition into a finished video via the HyperFrames renderer.

<render:Video id="final" composition={main.composition} semantic={speech.semantic}/>
Attribute Required Description
id yes Unique identifier
composition yes Composition from film:Film
semantic yes SemanticTrack from speech:Track

The renderer:

  1. Compiles the Composition into a HyperframesDocument — an HTML representation of every frame
  2. Renders each frame through Chrome/Chromium
  3. Encodes the frame sequence into video
  4. Mixes the audio Tracks
  5. Muxes video + audio into the final MP4

Output: {final.video} — the finished video as an ordinary content-addressed BlobArtifact. This is the most common Build Target, and it can also be connected directly to later Blob consumers such as media trimming, audio/frame extraction or a model reference input.

Full pipeline walkthrough

The complete data flow from Script to rendered video. This example is based on examples/talking-film-graph-check/ — the smallest complete graph.

Author Source (main.svml)

<?svml using="@hypit/markup@1"?>

<svml>
  <import from="@hypit/script@1"/>
  <import as="wording" from="@hypit/text@1"/>
  <import as="gpt" from="@hypit/gpt-image@1"/>
  <import as="seedance" from="@hypit/seedance@1"/>
  <import as="pipeline" from="@hypit/media-pipeline@1"/>
  <import as="speech" from="@hypit/speech-track@1"/>
  <import as="whisperx" from="@hypit/whisperx@1"/>
  <import as="caption" from="@hypit/caption@1"/>
  <import as="caption-fine" from="@hypit/caption-fine@1"/>
  <import as="fonts" from="@hypit/fonts-open@1"/>
  <import as="media-track" from="@hypit/media-track@1"/>
  <import as="text" from="@hypit/typography-track@1"/>
  <import as="space" from="@hypit/spatial@1"/>
  <import as="program" from="@hypit/program-space@1"/>
  <import as="film" from="@hypit/film@1"/>
  <import as="render" from="@hypit/render-hyperframes@1"/>
  <import as="recipes" source="./recipes.svs"/>

  <!-- 1. Script: the semantic truth -->
  <script id="story">
    <opening><HOST>Meaning @demo becomes the source @/demo.</opening>
  </script>

  <!-- 2. Generation: Seedance talking head + standalone video -->
  <wording:Value id="direction">
    Locked medium close-up in a quiet daylight studio. Spoken dialogue — say exactly: Meaning becomes the source.
  </wording:Value>
  <wording:Value id="scene-look">
    A photograph with the texture of real iPhone footage. Generate a vertical seated medium
    close-up, as one frame cut out of video actually shot on an iPhone: genuinely real rather than
    glossy, carrying the texture of video and not of a posed photograph. The background stays clearly
    visible, with no depth-of-field blur. Skin texture is fine and real, the light is natural, and no
    part of the picture is broken. One presenter at a desk in a quiet daylight studio.
  </wording:Value>
  <gpt:Image id="studio-scene" prompt={scene-look} aspect-ratio="9:16" resolution="2K"/>
  <seedance:ReferenceVideo id="take" model="mini"
    prompt={direction} duration="5" generate-audio="true">
    <seedance:Reference image={studio-scene.image}/>
  </seedance:ReferenceVideo>
  <seedance:ReferenceVideo id="motion" model="mini"
    prompt={direction} duration="5">
    <seedance:Reference image={studio-scene.image}/>
  </seedance:ReferenceVideo>

  <space:Canvas id="vertical" width="1080" height="1920"/>
  <program:Clock id="clock" frame-rate="30"/>
  <space:Frame id="speech-frame" within={vertical}
    left="0%" top="0%" right="100%" bottom="100%"/>
  <space:Frame id="title-frame" within={vertical}
    left="6%" top="6%" right="94%" bottom="16%"/>
  <space:Frame id="card-frame" within={vertical}
    left="10%" top="20%" right="90%" bottom="70%"/>

  <!-- 3. Timing: normalize and align the Segment before assembly -->
  <pipeline:Normalize id="take-media" source={take.video}
    video="primary-moving" audio="default" span-authority="video" clock={clock}/>
  <pipeline:Normalize id="motion-media" source={motion.video}
    video="primary-moving" audio="none" span-authority="video" clock={clock}/>
  <whisperx:SemanticTake id="opening-semantic" narrative={story}
    segment={story.segment.opening} media={take-media.media} language="en"/>
  <speech:Track id="speech"
    visual-frame={speech-frame} visual-appearance={recipes.speech.visual} visual-z="0">
    <speech:Take source={opening-semantic.take}/>
  </speech:Track>

  <!-- 4. Tracks: captions, Media, text -->
  <fonts:Stack id="caption-font" family="inter" weight="700" style="normal"/>
  <fonts:Stack id="title-font" family="inter" weight="900" style="normal"/>
  <caption-fine:Style id="base-caption" recipe={recipes.caption.base} font={caption-font}/>
  <caption:Program id="caption-program" document={story.caption} narrative={story}
    default={base-caption}/>
  <caption-fine:Track id="captions" document={story.caption}
    semantic={speech.semantic} program={caption-program}/>

  <media-track:Track id="cards" semantic={speech.semantic} canvas={vertical}>
    <media-track:Item media={motion-media.media} during={story.selection.demo}
      frame={card-frame} appearance={recipes.media.card} motion={recipes.motion.card}/>
  </media-track:Track>
  <text:Style id="title-style" recipe={recipes.text.title} font={title-font}/>
  <text:Track id="titles" semantic={speech.semantic}>
    <text:Area id="meaning" placement={title-frame} style={title-style} during="program">
      MEANING
    </text:Area>
  </text:Track>

  <!-- 5. Film: compose all tracks -->
  <film:Film id="main" canvas={vertical} semantic={speech.semantic}
    appearance={recipes.film.vertical}>
    <film:Track source={speech.visual}/>
    <film:Track source={speech.audio}/>
    <film:Track source={cards.visual}/>
    <film:Track source={captions.track}/>
    <film:Track source={titles.track}/>
  </film:Film>

  <!-- 6. Render: compile to MP4 -->
  <render:Video id="final" composition={main.composition}
    semantic={speech.semantic}/>
</svml>

right and bottom are absolute edge positions, not insets. A Frame spanning the middle 80% of its parent is left="10%" right="90%", not left="10%" right="10%" — the second resolves to zero width and is rejected.

Stylesheet (recipes.svs)

<?svml using="@hypit/svs@1"?>

<sheet version="1">
  film.vertical {
    background: #09090B;
  }
  speech.visual { fit: cover; }
  media.card {
    stack-order: 40; fit: cover; playback: hold-start;
    frame-paint: #111116; clip: rounded; radius: 20;
  }
  motion.card {
    enter: slide; enter-frames: 4; enter-direction: up; enter-easing: ease-out;
    exit: fade; exit-frames: 4; exit-easing: ease-in;
  }
  caption.base {
    stack-order: 70; x: 0.08; y: 0.76; width: 0.84;
    size: 58; line-height: 1; align: center;
    fill: #FFFFFF; background: #09090BCC; padding: 16 24; radius: 18;
  }
  text.title {
    stack-order: 90;
    font: Inter; weight: 900; size: 64; align: center;
    fill: #FFFFFF; tracking: -1;
  }
</sheet>

Run Source (build.svrun)

<?svml using="@hypit/run-markup@1"?>

<svrun version="1">
  <author source="./main.svml"/>
  <target output="final.video"/>
</svrun>

Compile and verify

hypit check examples/talking-film-graph-check/main.svml

hypit plan examples/talking-film-graph-check/build.svrun

check compiles the Author Graph — validates all imports, types, and graph edges without calling any external service. plan additionally compiles the Run Source and outputs the frozen BuildPlan showing every Operation the Scheduler would issue. Inspect the plan before spending money.