Candidate construction guide · native Resolve not tested
Grow a document range and return the camera
An original SVG/code Remotion treatment with a complete manual DaVinci Resolve / Fusion construction guide. Two meaningful content variants share one framegraph. No source artwork, raster/video adaptation input or native Resolve project is shipped. Builder tests are not a rendered-quality approval.
Public kit: install and verify
The kit root contains core-publication-v08.zip, active-assets.zip,
handoff.py, guide.json, this README, controls.csv, fusion-controls.csv,
overlay-manifest.json, provenance.json and test_kit.py. The distributor
supplies the pinned core and handoff helper; neither a private worker nor a
repository checkout is required. No generator or prebuilt inputs helper is
needed: both payloads are embedded in the recipe.
Both ZIPs start with project/. From the kit root, unpack core first, then
the active overlay into the same kit root, not into project/ again:
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
PYTHONDONTWRITEBYTECODE=1 python3 test_kit.py --project project --core core-publication-v08.zip
Integrity contracts (SHA-256):
| Artifact | SHA-256 |
|---|---|
| Core ZIP | 307fb6b927e35e6d73779652873d994f5c3e36cb7deba65f22fa64748057a0de |
| Active ZIP | a670419b922bcf908f1d3f7edd30c1f10218e39dc4b3383c7960e9615be9e930 |
| Recipe | 0fb01c8278624841873044311fd8fcbfad622d368d78e09e6a856ff30e8d7276 |
| Guide | be26d71cfe62d38eb74345026774f1b3761ab59dfadb9e92769fcade65715713 |
| Handoff helper | dedfeaefb89a3158c060f092d9393fe208a52f88b51e21e8bd06c8b272a25721 |
overlay-manifest.json binds every active recipe/SVG ZIP member and every
guide/control/provenance artifact. The active ZIP has only this recipe and its
2 original SVGs, no core, dependencies or legacy art.
source.localMedia is research metadata, not a requested input path.
Remotion: preflight, then authorized rendering
Read project/README.md and project/AGENT_GUIDE.md. Use Node >=22.18, Python 3.11+,
FFmpeg/FFprobe and an explicitly installed supported Chromium. Preserve
project/public/fonts/Geist.ttf and its OFL.txt; do not replace the font or
silently shrink text. The core browser text-fit gate remains authoritative.
Set REMOTION_BROWSER_EXECUTABLE if your installed browser is not discovered.
No automatic browser download is required by these instructions.
From project/, the actual archived CLI is:
cd project
npm ci
node --experimental-strip-types --input-type=module -e '
import {prepareInput} from "./scripts/lib.mjs";
for (const variant of ["default", "adaptation"]) {
const prepared = await prepareInput({projectRoot: process.cwd(), recipeId: "canvas-range-camera-return", variant});
console.log(variant, prepared.expected);
}
'
node --experimental-strip-types scripts/render.mjs --recipe canvas-range-camera-return --variant default --out out/canvas-range-camera-return-default.mp4
node --experimental-strip-types scripts/render.mjs --recipe canvas-range-camera-return --variant adaptation --out out/canvas-range-camera-return-adaptation.mp4
node --experimental-strip-types scripts/verify.mjs out/canvas-range-camera-return-default.mp4
node --experimental-strip-types scripts/verify.mjs out/canvas-range-camera-return-adaptation.mp4
Expected: 1920×1080, 24/1 fps, 144 frames, 6 seconds
(6.000000000), zero audio streams. The renderer refuses an existing
MP4 or its sibling manifest; choose a new basename instead of overwriting. Keep
each generated .manifest.json with its output. These commands are for the
authorized renderer: this builder did not encode or review candidate output.
Content substitutions
Default: editable weekly studio sessions. Adaptation: editable weekly garden tasks. Both contain original code-like text, a distinct brief, context and SVG document mark. The code is displayed text, not an executed app or an assertion of product functionality.
Every slot, both literal payloads, consuming layers and field contracts appear
under guide.json.editableSubstitutions. Keep the same number of rows/items,
literal line breaks, aspect ratios and range/camera parenting. Character limits
are not a font-fit guarantee. For custom SVGs use original permitted geometry,
no <image>, raster data URI, external resource, script, embedded font or source
frame. Inspect both content and framing after every edit.
From project/, create a complete Content object, not a partial override:
python3 - <<'PY'
import json
from pathlib import Path
recipe = json.loads(Path("recipes/canvas-range-camera-return.json").read_text())
content = dict(recipe["defaults"])
content["attachment"] = "Planning brief"
Path("custom.json").write_text(json.dumps(content, indent=2) + "\n")
PY
node --experimental-strip-types --input-type=module -e '
import {prepareInput} from "./scripts/lib.mjs";
console.log((await prepareInput({projectRoot: process.cwd(), recipeId: "canvas-range-camera-return", contentPath: "custom.json"})).expected);
'
node --experimental-strip-types scripts/render.mjs --recipe canvas-range-camera-return --content custom.json --out out/canvas-range-camera-return-custom.mp4
--content and --variant are mutually exclusive. Relative content paths are
resolved against the process working directory; the example deliberately runs
inside project/. The frozen guide verifies the embedded variants. A changed
recipe or SVG requires a revised hash contract; do not relabel it as this freeze.
Source clock, observation and adaptation
Film: Prototyping with canvas in ChatGPT. Retained official-player publication:
2025-04-08, timestamp
2025-04-08T12:25:22-07:00. This is pinned local
metadata, not a new live web verification. Selection window: September 5, 2024
through September 5, 2026 at 17:13:40.602033 UTC; no later publication is used.
Source interval [1608, 1752) at
24/1, with exact nominal source seconds
[67, 73).
Local frame zero = source 1608; last local frame 143 =
source 1751. Excerpt offset is zero. Source-relative
PTS and durations are retained verbatim in guide.json.sourceClock; every
excerpt PTS was separately compared as a rational time. Absolute master PTS and
coded-tail duration were not recovered. Do not round this clock or stretch it
to another timeline rate.
Research excerpt SHA-256: 8c0edacf13856e57e0e98c8395876c545ee812edce70d61fa245295bb400e208.
The source master SHA is inherited, not freshly rehashed. The source/timing
unit and related implementation file hashes are in provenance.json.
Related Edit/SelectionContext and its partial
Resolve guide supplied parenting/masking concepts, not a complete adapter.
Inspection: all 144 excerpt frames viewed statically as consecutively decoded 480×270 thumbnails, plus 6 selected 1920×1080 native-size stills. Contact sheets are not moving review or all-frame full-resolution inspection. No whole-film/new-coverage claim, native Resolve execution, exact source-quality approval or measured OpenAI props is made.
Authored choreography
- 0–3: wide browser workspace. 3–12: the common camera reaches 1.92 scale, translated by (−616.4,−409.4) design pixels. This is an authored focus mapping, not recovered camera calibration.
- Selection row starts are 13/15/17/20/23/26/29/32/35. Each highlight grows for two frames. The last endpoint reaches its full width at 37. Text, line numbers, range masks, pointer and contextual action all remain inside the same document/workspace/camera hierarchy.
- 48–124: contextual action remains attached below the range. Camera holds close through 97, returns via scale 1.30 at 108 and 1.025 at 120, then reaches 1 at 134.
- The range ends at 121, after the main return while a small settling displacement remains. The contextual action ends at 125. The original text remains unchanged through 143; no request entry, send or rewritten result is invented.
Visible-state brackets are source observations/estimates; all numeric tracks are
chosen adaptation values. guide.json.observationPolicy keeps those categories
separate. The following samples are checkpoints to inspect, not assertions
that a rendered output already passed:
| Local frame | Source frame | Nominal source seconds | Expected authored state |
|---|---|---|---|
| 0 | 1608 | 67.000000 | Wide workspace, original document. |
| 3 | 1611 | 67.125000 | Last wide hold frame. |
| 12 | 1620 | 67.500000 | Close camera; no range yet. |
| 13 | 1621 | 67.541667 | First range row begins growing. |
| 16 | 1624 | 67.666667 | Second range row completing. |
| 24 | 1632 | 68.000000 | Middle of selection growth. |
| 35 | 1643 | 68.458333 | Final range row begins. |
| 37 | 1645 | 68.541667 | Full nine-row selection. |
| 48 | 1656 | 69.000000 | Context action disclosed below range. |
| 97 | 1705 | 71.041667 | Last close hold before return. |
| 100 | 1708 | 71.166667 | Camera returning; range remains parented. |
| 108 | 1716 | 71.500000 | Intermediate wide return. |
| 120 | 1728 | 72.000000 | Range still present, camera near wide. |
| 121 | 1729 | 72.041667 | Range clears; contextual action still present. |
| 125 | 1733 | 72.208333 | Contextual action removed. |
| 134 | 1742 | 72.583333 | Camera reaches original overview. |
| 143 | 1751 | 72.958333 | Unchanged document at cutoff, no sent request. |
Resolve / Fusion construction
- Create a 1920×1080, square-pixel composition at 24/1, local frames 0–143 inclusive. Set the actual rational cadence, not a rounded integer substitute. Do not add a final hold. Keep audio empty.
- Work on transparent 4096×4096 domains, with design pixel (0,0) placed at
work pixel (1024,1024). Parent/child domains stay identical. Build the
opaque
RootPaperBackground and nodes listed below in order; later siblings are foreground.guide.json.rootPipelinesupplies its color/domain, final crop coordinates and MediaOut connection.nativePalettesupplies normalized RGB values for editable colors in both variants; use opacity separately. - For each layer build
Construct → LocalMask → AnchorShift → ScaleRotate → Position → Gated → Merge.Foreground; the previous sibling feedsMerge.Background. For a group,Constructis its ordered child merges over a transparent background. Apply group alpha once after assembly. guide.json.fusionNodesnames every tool, connection, parent, raw/static control, content binding and gate.svgSubgraphsenumerates every original SVG Rectangle/Ellipse/Polygon, color, alpha and merge order for both variants. Build those natively; do not rasterize SVGs or use source footage as Loaders.- Bake the numeric rows in
fusion-controls.csvat each integer local frame. Its AnchorShift/Position/ScaleRotate/Gated columns are derived below.controls.csvcontains every raw property sample, including independent width/radius tracks. Neither CSV is a magically importable.settingfile: transfer columns to the named native controls using frame-based keys. - Install the licensed bundled Geist. Use Text+ for text, the explicit weight,
pixel box, line advance, tracking and alignment in each node. Text+ Size
fontSize/4096is only an uncalibrated starting estimate, not a verified CSS conversion. Compare glyph footprint, baseline and fit natively before delivery. Retain literal line breaks and stepped character reveals. Never scale type down just to hide overflow. - After the last root merge, crop exactly work pixels x=[1024,2944), y=[1024,2104) to 1920×1080; do not resize. Send to MediaOut. Keep anything outside the final crop until this final operation, except declared local masks.
Coordinates and interpolation
- Code: parent-local pixels, +X right, +Y down, rotation clockwise. Origin defaults to (0,0), not the layer center. A group width/height is neither a child scale nor an implicit clip. Only declared overflow/clip masks clip.
- Fusion normalized point =
((x+1024)/4096, 1-(y+1024)/4096). For a mask with top-left (x,y) and size (w,h), use center((x+w/2+1024)/4096, 1-(y+h/2+1024)/4096)and size(w/4096,h/4096). - Exact uniform layer transform:
p' = t + o + Rcw(angle)*scale*(p-o). Letc=(1024,1024)in design space. AnchorShift translates byc-o; ScaleRotate uses center/pivot (0.5,0.5), Size=scale and Angle=−rotation; Position translates byt+o-c. Translation(dx,dy)means Transform Center(0.5+dx/4096,0.5-dy/4096). Do not also put layer X/Y on a mask and double it. - Apply overflow and clip in the layer's local coordinates before its own
transform. Rounded rectangle radius r uses the union of central rectangles
(r,0,w-2r,h),(0,r,w,h-2r)and four radius-r circles at inset corners. Clamp union alpha; do not add paint opacity. r=0 is one rectangle. CSS border is inward: outer stroke-color mask, then fill inset by strokeWidth with inner radiusmax(0,r-strokeWidth). Ellipses use their actual width/height. - SVG contain factor =
min(boxWidth/viewBoxWidth,boxHeight/viewBoxHeight); centered leftover margins are added before the layer transform. Polygon vertices are explicit source SVG numbers, not hand-traced film shapes. - Every track uses the global local frame, including children that start
later. Keys clamp outside their range. The left key owns easing:
linear
t, smootht*t*(3-2*t), hold stays at its left value until the right key's frame. Use per-frame baked keys, not an approximate native ease preset. - Merge Blend = active gate × local opacity. A child is visible only while all ancestor gates are active. Do not multiply parent opacity into each child: parent alpha belongs after its completed child composite.
Treatment-specific hierarchy
Build document text and all nine range masks in document space before the common camera transform. Do not place highlights in output/screen space. Keep the document aperture at its own parent, so it transforms with the workspace. Row mask widths are independent numeric controls: a changed line requires a deliberate endpoint check, not a screen-space compensating key.
RootPaper -> ordered root merges -> FinalCrop -> MediaOut
backdrop [rect] -> backdrop_Merge
camera [group] -> camera_Merge
workspace [group] -> workspace_Merge
window-paper [rect] -> window-paper_Merge
tab-strip [rect] -> tab-strip_Merge
active-tab [rect] -> active-tab_Merge
tab-title [text] -> tab-title_Merge
address-bar [rect] -> address-bar_Merge
address [text] -> address_Merge
side-paper [rect] -> side-paper_Merge
brand [text] -> brand_Merge
brief-box [rect] -> brief-box_Merge
brief-icon [image] -> brief-icon_Merge
attachment [text] -> attachment_Merge
request-box [rect] -> request-box_Merge
request [text] -> request_Merge
draft-status [text] -> draft-status_Merge
document-link [rect] -> document-link_Merge
document-link-label [text] -> document-link-label_Merge
response [text] -> response_Merge
ask-box [rect] -> ask-box_Merge
ask-placeholder [text] -> ask-placeholder_Merge
ask-plus [path] -> ask-plus_Merge
voice-disc [ellipse] -> voice-disc_Merge
document-heading [text] -> document-heading_Merge
preview-button [rect] -> preview-button_Merge
preview-label [text] -> preview-label_Merge
document [group] -> document_Merge
document-paper [rect] -> document-paper_Merge
code [group] -> code_Merge
pre-number-0 [text] -> pre-number-0_Merge
pre-line-0 [text] -> pre-line-0_Merge
pre-number-1 [text] -> pre-number-1_Merge
pre-line-1 [text] -> pre-line-1_Merge
pre-number-2 [text] -> pre-number-2_Merge
pre-line-2 [text] -> pre-line-2_Merge
pre-number-3 [text] -> pre-number-3_Merge
pre-line-3 [text] -> pre-line-3_Merge
pre-number-4 [text] -> pre-number-4_Merge
pre-line-4 [text] -> pre-line-4_Merge
pre-number-5 [text] -> pre-number-5_Merge
pre-line-5 [text] -> pre-line-5_Merge
pre-number-6 [text] -> pre-number-6_Merge
pre-line-6 [text] -> pre-line-6_Merge
pre-number-7 [text] -> pre-number-7_Merge
pre-line-7 [text] -> pre-line-7_Merge
range-row-1 [rect] -> range-row-1_Merge
range-row-2 [rect] -> range-row-2_Merge
range-row-3 [rect] -> range-row-3_Merge
range-row-4 [rect] -> range-row-4_Merge
range-row-5 [rect] -> range-row-5_Merge
range-row-6 [rect] -> range-row-6_Merge
range-row-7 [rect] -> range-row-7_Merge
range-row-8 [rect] -> range-row-8_Merge
range-row-9 [rect] -> range-row-9_Merge
row-number-1 [text] -> row-number-1_Merge
row-text-1 [text] -> row-text-1_Merge
row-number-2 [text] -> row-number-2_Merge
row-text-2 [text] -> row-text-2_Merge
row-number-3 [text] -> row-number-3_Merge
row-text-3 [text] -> row-text-3_Merge
row-number-4 [text] -> row-number-4_Merge
row-text-4 [text] -> row-text-4_Merge
row-number-5 [text] -> row-number-5_Merge
row-text-5 [text] -> row-text-5_Merge
row-number-6 [text] -> row-number-6_Merge
row-text-6 [text] -> row-text-6_Merge
row-number-7 [text] -> row-number-7_Merge
row-text-7 [text] -> row-text-7_Merge
row-number-8 [text] -> row-number-8_Merge
row-text-8 [text] -> row-text-8_Merge
row-number-9 [text] -> row-number-9_Merge
row-text-9 [text] -> row-text-9_Merge
post-number-0 [text] -> post-number-0_Merge
post-line-0 [text] -> post-line-0_Merge
post-number-1 [text] -> post-number-1_Merge
post-line-1 [text] -> post-line-1_Merge
post-number-2 [text] -> post-number-2_Merge
post-line-2 [text] -> post-line-2_Merge
post-number-3 [text] -> post-number-3_Merge
post-line-3 [text] -> post-line-3_Merge
post-number-4 [text] -> post-number-4_Merge
post-line-4 [text] -> post-line-4_Merge
post-number-5 [text] -> post-number-5_Merge
post-line-5 [text] -> post-line-5_Merge
post-number-6 [text] -> post-number-6_Merge
post-line-6 [text] -> post-line-6_Merge
post-number-7 [text] -> post-number-7_Merge
post-line-7 [text] -> post-line-7_Merge
post-number-8 [text] -> post-number-8_Merge
post-line-8 [text] -> post-line-8_Merge
post-number-9 [text] -> post-number-9_Merge
post-line-9 [text] -> post-line-9_Merge
post-number-10 [text] -> post-number-10_Merge
post-line-10 [text] -> post-line-10_Merge
post-number-11 [text] -> post-number-11_Merge
post-line-11 [text] -> post-line-11_Merge
post-number-12 [text] -> post-number-12_Merge
post-line-12 [text] -> post-line-12_Merge
post-number-13 [text] -> post-number-13_Merge
post-line-13 [text] -> post-line-13_Merge
range-action [group] -> range-action_Merge
action-shadow [rect] -> action-shadow_Merge
action-paper [rect] -> action-paper_Merge
action-plus [path] -> action-plus_Merge
action-label [text] -> action-label_Merge
selection-pointer [path] -> selection-pointer_Merge
chrome-dot-0 [ellipse] -> chrome-dot-0_Merge
chrome-dot-1 [ellipse] -> chrome-dot-1_Merge
chrome-dot-2 [ellipse] -> chrome-dot-2_Merge
Animated controls
Every static numeric/text/shape control remains in guide.json.fusionNodes;
this short table lists animated controls only. The complete raw key arrays and
per-frame values are in the guide and CSVs.
| Layer.property | Keys | Numeric minimum → maximum |
|---|---|---|
camera.x |
8 | -616.4 → 0 |
camera.y |
8 | -409.4 → 0 |
camera.scale |
8 | 1 → 1.92 |
range-row-1.width |
2 | 0.01 → 170 |
range-row-2.width |
2 | 0.01 → 432 |
range-row-3.width |
2 | 0.01 → 432 |
range-row-4.width |
2 | 0.01 → 432 |
range-row-5.width |
2 | 0.01 → 432 |
range-row-6.width |
2 | 0.01 → 432 |
range-row-7.width |
2 | 0.01 → 432 |
range-row-8.width |
2 | 0.01 → 432 |
range-row-9.width |
2 | 0.01 → 24 |
selection-pointer.x |
11 | 84 → 709 |
selection-pointer.y |
11 | 270 → 516 |
Differences and acceptance limits
The source syntax coloring and monospaced font are replaced by live proportional Geist and simplified row coloring. Highlight widths, pointer coordinates and camera/easing values are authored. The source has changing glyph-level endpoints; this kit uses nine editable row masks with chosen endpoints. Native text shaping and apparent camera blur remain unvalidated.
The builder has not executed Resolve or reviewed moving candidate output. Archive hashes, schema checks, sampled controls and custom-content preflights cannot validate native antialiasing, Text+ layout, browser shaping, occlusion readability or motion quality. Parent rendering and independent comparison are separate acceptance gates. Source quality remains false until that process explicitly establishes otherwise. No corpus or publication count is changed.
Optional provenance audits may resolve the repo: and accepted-stage:
bindings in provenance.json against the retained research archive. Those
historical sources are not needed to install, preflight, render or manually
construct this public kit, and may never become adaptation inputs.