OpenAI Film Fieldnotes

Candidate construction guide · native Resolve not tested

Suggestions become one request

An original SVG/code Remotion recipe and a complete manual DaVinci Resolve / Fusion handoff. One source-grounded treatment, two original content variants. This is a candidate, not source-fidelity acceptance or a tested native .setting. This worker did not render an output or run Resolve. Later parent renders need their own hashes/review; this statement is not a claim about the parent's current queue.

Public download: prepare once

Use a fresh kit directory. The public package supplies core-publication-v08.zip, active-assets.zip, handoff.py, this README, guide.json, controls.csv, fusion-controls.csv, overlay-manifest.json, provenance.json, and test_kit.py. No private worker, repository checkout, generator, source clip or inputs/ helper is needed. This overlay contains only this recipe and its active original SVGs. Both content variants are embedded in the recipe. Do not add old raster/video ZIPs.

# Run in the downloaded kit root. Both ZIPs contain project/ at their top level.
python3 -m zipfile -e core-publication-v08.zip .
python3 -m zipfile -e active-assets.zip .
python3 handoff.py verify --guide guide.json --project project
python3 handoff.py frames --guide guide.json --project project > controls-regenerated.csv
cmp controls.csv controls-regenerated.csv

Do not unpack either ZIP into project/ again: that creates project/project/. Core first, overlay second. The public tests verify the exact ZIP members and hashes; the handoff command verifies the recipe, native clock and selected SVG bytes only.

Remotion: parent or recipient renders

Prerequisites: Node ≥22.18, Python 3, FFmpeg/FFprobe and an explicitly installed supported Chromium. Preserve the pinned package lock and core README. Rendering does not auto-download a browser; set REMOTION_BROWSER_EXECUTABLE to its existing executable when needed. Do not place persistent browser profiles in an evidence tree.

cd project
npm ci
node --experimental-strip-types scripts/render.mjs --recipe pulse-prompt-collapse --variant default --out out/pulse-prompt-collapse-default.mp4
node --experimental-strip-types scripts/render.mjs --recipe pulse-prompt-collapse --variant adaptation --out out/pulse-prompt-collapse-adaptation.mp4
node --experimental-strip-types scripts/verify.mjs out/pulse-prompt-collapse-default.mp4
node --experimental-strip-types scripts/verify.mjs out/pulse-prompt-collapse-adaptation.mp4

Expected: 1920×1080, 60/1 fps, 420 frames, no audio. Keep each adjacent .manifest.json with its MP4. The renderer refuses pre-existing outputs; use a fresh filename for each revision. Do not add holds to synchronized comparisons. These are the actual commands from the archived renderer, not a hypothetical API.

Change the content, not the clock

default: paper objects, studio rituals and a weekly sketching request; adaptation: garden colours, evening skies and a monthly colour-study request. The final typed copy differs and remains deliberately incomplete in both.

Every string/color/media slot and its contract is in recipe.fields and in guide.json → editableSubstitutions. Text is live Geist from the core's public/fonts/Geist.ttf; retain public/fonts/OFL.txt. Do not use a system fallback. SVG art has no text, raster, external links or font dependency. Replace it with your own standalone SVG at the declared viewport/aspect ratio and with an accurate rights declaration. Do not flatten or trace source footage.

To make a custom JSON, run from project/:

python3 - <<'PY'
import json
from pathlib import Path
recipe = json.loads(Path('recipes/pulse-prompt-collapse.json').read_text())
content = recipe['defaults']
content['typed'] = 'Make a weekly notebook drawing prompt.'
Path('inputs').mkdir(exist_ok=True)
Path('inputs/my-content.json').write_text(json.dumps(content, indent=2) + '\n')
PY
node --experimental-strip-types --input-type=module - <<'JS'
import {prepareInput} from './scripts/lib.mjs';
const checked = await prepareInput({projectRoot: process.cwd(), recipeId: 'pulse-prompt-collapse', contentPath: 'inputs/my-content.json'});
console.log(checked.expected, checked.assets);
JS
node --experimental-strip-types scripts/render.mjs --recipe pulse-prompt-collapse --content inputs/my-content.json --out out/my-content.mp4
node --experimental-strip-types scripts/verify.mjs out/my-content.mp4

The custom-content preflight does not render, measure browser glyphs or execute Resolve. It checks the real core input contract and asset bytes. The renderer's font gate then rejects overflowing lines; shorten copy, never shrink or retime it. Custom assets are no longer the frozen guide.svgAssets contract: retain their new render manifest and review the substitution. handoff.py verify still verifies the two frozen built-in variants, not an arbitrary custom JSON. SVG-only policy is stricter than the core's general media API; run the supplied test's SVG checker or inspect replacement SVG resources before using new artwork.

The offline tests also check nominal advances from the actual archived Geist cmap/hmtx tables and all text-box heights. This is an unshaped default-font metric check, not the renderer's browser shaping or a Resolve Text+ fit verdict.

Source and clock

Research film: Introducing ChatGPT Pulse. The film link, publication date (2025-09-25) and master SHA are retained queue metadata, not a new online lookup. Only the accepted excerpt was rehashed and inspected. Master SHA: 1f5b53975635fd21b69f85194b35559a0397c69343acc89235acebe4e8fd0966 (inherited, not rehashed). Excerpt SHA: cf1328d3b99d1173d1275d559d2d5203ebc35b8c73d2f9693464d18f7b73bd82.

Source window [3060, 3480) = 51–58 seconds. Local frame f maps to source frame 3060 + f and excerpt frame f (zero lead offset). Seconds are exact frame/60 arithmetic, not newly measured source PTS. Header/decode checks confirm 60/1 and 420 consecutive excerpt frames. The retained study's 2ms PTS statement is inherited, not a new master-PTS check.

All frames were viewed statically in ordered 480×270 labeled sheets; selected boundary/cutoff stills were also viewed at native excerpt resolution 960×540. That is not moving-source review, not full-resolution viewing of every frame, not full-film inspection, and not evidence of source-quality equivalence. provenance.json binds each sheet range/hash and native still ordinal/hash without shipping research images as presentation inputs. Positions, easing and styling below are chosen adaptation values; only explicitly labeled cut observations are frame-exact.

Choreography

  1. 0–55: retain the phone-footer context and a 39–52 button press/release. At 56 cut to suggestions. Do not trim this lead to make the collapse start earlier.
  2. 56–88: stack scales .84→1 around (960,540). Rows are 1400×168px, pitch 204px, inside a fixed clip; visible text is live.
  3. 120–144: list shifts −204px; the initially lower row becomes central. 210–234: shift reaches −408px and the free-note row becomes central. A fourth row can overlap the visibility fringe during travel; this is one stack treatment.
  4. 278–298: top/bottom rows clear while the middle surface remains. 298–309: its leading mark fades; 300–312: its placeholder moves 168→62px locally. Controls grow separately at 310–323.
  5. 324–361: empty placeholder/caret phase. At 362, placeholder and microphone/voice bars switch off and send arrow/typed layer switch on. Characters arrive at 363+2n, n=0…28. Local 419 shows only 29 characters, not the entire supplied sentence; no submit follows.

Fusion: complete manual construction

  1. Set composition/output 1920×1080, square pixels, 60 fps, local frames 0–419 inclusive. Disable optical flow, motion blur and automatic retiming. No MediaIn of the research clip. resolveApplicationVerified remains false.
  2. Work every intermediate branch on a transparent 4096×4096 square domain, padding the design origin by (1024,1024). This preserves negative positions and child overflow. Do not crop each branch to 1920×1080. The phone viewport clips long content before parent camera magnification. Final crop only after the root merge: design rectangle starts at (1024,1024), size 1920×1080, with no resize. Composite over opaque white RootPaper, then MediaOut1.
  3. Construct the hierarchy below back to front at every level. Each child finishes into its parent's transparent merge chain. The last child output feeds that group's Construct, then its own mask/transform/alpha. guide.fusionNodes lists every node, connection, raw/static control, parent and effective frame gate. guide.svgSubgraphs lists every original primitive, geometry, fill and connection for both variants. Instantiate native Rectangle/Ellipse/Polygon + Background + Merge branches; no raster stand-in. SVG contain fitting precedes its box transform.
  4. Coordinate rule: recipe pixels use top-left origin, +X right, +Y down. Fusion mask position is ((x+1024)/4096, 1-(y+1024)/4096); width/height normalize by 4096. Local geometry is built before its layer transform. Radius is pixels: clamp to half the smaller dimension. Build rounded masks as a union of a (r,0,w−2r,h) rectangle, a (0,r,w,h−2r) rectangle and four radius-r circles centered at (r,r), (w−r,r), (r,h−r), (w−r,h−r). Union/clamp mask alpha; do not sum overlapping paint. At r=0 use one rectangle. Polygon vertices use the same normalization; preserve path subpath winding/holes. Intersect overflow:hidden rounded bounds and clip insets locally. A plain group width/height or radius does not clip unless declared. Borders are inside the box.
  5. Exact transform order avoids Fusion pivot ambiguity. Let local origin o be layer.origin, default (0,0), and working design center c=(1024,1024). AnchorShift translates by c−o; ScaleRotate has Center/Pivot (.5,.5), Size=scale, Angle=−rotation; Position translates by (x,y)+o−c. A translation (dx,dy) has Center (.5+dx/4096, .5−dy/4096). This gives parentPoint=(x,y)+o+R_clockwise(rotation)*scale*(point−o). These kits use uniform scale only. Apply Gated Merge Blend=active*opacity once after assembly, not to each child. Active is 1 only when the layer and every ancestor contain the local frame. End gates are exclusive.
  6. Text+ uses the exact live content, font weight, box, line advance and tracking in each node's textControls. The listed fontSize/4096 is only a starting Size estimate, not a calibrated conversion. Match actual glyph footprint and baseline using licensed Geist before review. Do not auto-fit, outline source lettering, or imply native typography equivalence. Reveal steps key StyledText on the same global frame clock, including prefix/suffix. No child-clock reset.
  7. controls.csv gives every present recipe numeric property at every native frame; fusion-controls.csv additionally gives every layer's numeric AnchorShift, ScaleRotate, Position, Blend, size, radius and clip inputs at every frame. Use those values as frame-by-frame linear baked keys; they are not measured source controls. Between sparse recipe keys, ease according to the left key: smooth=t*t*(3-2*t), linear=t, hold=left value until the exact right key. A Bézier preset is not a substitute. Preserve the declared one-frame cuts.
  8. Select all content/colors/art from one variant. Check all checkpoints, then compare an actual Resolve render against Remotion and the allowed source excerpt at native cadence. Native Resolve execution and moving comparison remain open.
RootPaper (opaque white working-domain Background)
phone-context [group] -> phone-context_Merge
  intro-shell [rect] -> intro-shell_Merge
  intro-screen [rect] -> intro-screen_Merge
  intro-card-fragment [rect] -> intro-card-fragment_Merge
  intro-mark [image] -> intro-mark_Merge
  intro-heading [text] -> intro-heading_Merge
  intro-body [text] -> intro-body_Merge
  intro-question [text] -> intro-question_Merge
  intro-action [group] -> intro-action_Merge
    intro-action-bg [rect] -> intro-action-bg_Merge
    intro-action-text [text] -> intro-action-text_Merge
    intro-action-mark [image] -> intro-action-mark_Merge
suggestion-scene [group] -> suggestion-scene_Merge
  stack-viewport [group] -> stack-viewport_Merge
    cycling-list [group] -> cycling-list_Merge
      row-0 [group] -> row-0_Merge
        row-0-surface [rect] -> row-0-surface_Merge
        row-0-mark [image] -> row-0-mark_Merge
        row-0-copy [text] -> row-0-copy_Merge
      row-1 [group] -> row-1_Merge
        row-1-surface [rect] -> row-1-surface_Merge
        row-1-mark [image] -> row-1-mark_Merge
        row-1-copy [text] -> row-1-copy_Merge
        row-1-topic [text] -> row-1-topic_Merge
      row-2 [group] -> row-2_Merge
        row-2-surface [rect] -> row-2-surface_Merge
        row-2-mark [image] -> row-2-mark_Merge
        row-2-copy [text] -> row-2-copy_Merge
        row-2-topic [text] -> row-2-topic_Merge
      row-3 [group] -> row-3_Merge
        row-3-surface [rect] -> row-3-surface_Merge
        row-3-mark [image] -> row-3-mark_Merge
        row-3-copy [text] -> row-3-copy_Merge
        typed-request [text] -> typed-request_Merge
        idle-caret [rect] -> idle-caret_Merge
        composer-voice [group] -> composer-voice_Merge
          voice-disc [ellipse] -> voice-disc_Merge
          voice-bar-0 [rect] -> voice-bar-0_Merge
          voice-bar-1 [rect] -> voice-bar-1_Merge
          voice-bar-2 [rect] -> voice-bar-2_Merge
          voice-bar-3 [rect] -> voice-bar-3_Merge
          send-arrow [path] -> send-arrow_Merge
        composer-microphone [group] -> composer-microphone_Merge
          mic-capsule [rect] -> mic-capsule_Merge
          mic-base [path] -> mic-base_Merge
      row-4 [group] -> row-4_Merge
        row-4-surface [rect] -> row-4-surface_Merge
        row-4-mark [image] -> row-4-mark_Merge
        row-4-copy [text] -> row-4-copy_Merge
Final crop (1024,1024,1920,1080; no resize) -> MediaOut1

Animated controls

All static values and every original key are also present in guide.json.

Layer.property Keys Authored value range
intro-action.scale 4 0.96 → 1
suggestion-scene.scale 2 0.84 → 1
suggestion-scene.opacity 2 0.55 → 1
cycling-list.y 5 -408 → 0
row-0.opacity 3 0 → 0.42
row-1.opacity 5 0 → 1
row-1-topic.opacity 3 0 → 1
row-2.opacity 7 0 → 1
row-2-topic.opacity 4 0 → 1
row-3.opacity 5 0 → 1
row-3-mark.opacity 3 0 → 1
row-3-copy.x 3 62 → 168
idle-caret.opacity 3 0 → 1
composer-voice.scale 2 0.02 → 1
composer-microphone.opacity 2 0 → 1
row-4.opacity 5 0 → 0.42

Checkpoints

Local / excerpt frame Source frame Frame-derived source seconds Expected candidate state
0 3060 51.000000 Phone footer, not suggestion rows.
40 3100 51.666667 Footer button press phase.
55 3115 51.916667 Last phone-context frame.
56 3116 51.933333 Cut to suggestion abstraction.
88 3148 52.466667 Stack at full authored scale.
120 3180 53.000000 First upward list cycle begins.
140 3200 53.333333 Departing top row gone.
144 3204 53.400000 Second subject occupies the center.
210 3270 54.500000 Second upward cycle begins.
230 3290 54.833333 Former first selected row gone.
234 3294 54.900000 Free-note row at the center.
278 3338 55.633333 Peripheral fade begins.
298 3358 55.966667 Peripheral rows inactive.
300 3360 56.000000 Leading mark clears as placeholder starts shifting.
309 3369 56.150000 No leading mark.
310 3370 56.166667 Separate voice/microphone growth begins.
323 3383 56.383333 Controls full size.
324 3384 56.400000 Placeholder focus/caret phase.
361 3421 57.016667 Last placeholder frame.
362 3422 57.033333 Blank typed layer, send arrow; no first character yet.
363 3423 57.050000 First original character.
391 3451 57.516667 15 characters visible.
419 3479 57.983333 29 characters, partial word at source cutoff; no send.

Differences and open review

Largest gap: Suggestion words, symbols, press response, row opacity curves and input cadence are original approximations. The source uses a wand mark and different content; this kit is a promotional motion specimen, not proof of a shipping suggestion/composer interaction.

Common open gates: browser text shaping, antialiasing, actual moving-output comparison, native Fusion text/mask calibration, and native Resolve execution. No source image was traced or flattened. No source-master download, video render, dependency copy, deployment, shared-state edit or completion-count credit is part of this build.

Immutable preparation contract

From kit root, after preparation, run python3 test_kit.py --project project --core core-publication-v08.zip. This runs focused offline checks and actual-core default/adaptation/custom-content preflights, with negative content cases. No browser, Resolve or video encoding. Repository/provenance replay is optional historical auditing; source clips and the private worker are never prerequisites for rendering or this public handoff.