At a glance
| Beat | HyperFrames + Three.js | Blender 5.2 | EffectCraft 0.3.1 | Tesseract 0.3.1 |
|---|---|---|---|---|
| Glyph field + DoF rack | ● done shader bokeh | ● done real camera DoF | ○ no DoF bug under Advanced 3D | ● done JS-projected blur; space-bunny: per-instance blur in one WGSL shader |
| Haze | ◐ faked depth fade | ● done world volume | ○ none | ◐ faked depth fade |
| Swarm → SIGNAL at 3.0 s | ● done | ● done | ● done | ● done |
| Motion blur | ● done velocity-stretched sprites | ● done EEVEE, real | ● done layer MB, 8 samples | ◐ stepped 8 ghost copies |
| Bars on 12 beats | ● done | ● done | ● done extruded | ◐ flat billboards, no extrusion |
| God rays | ◐ approx. soft glow | ◐ approx. volumetric glow | ◐ approx. faint | ○ not visible |
| Burst at 14.0 s | ● done | ● done | ● done | ● done |
| Render, 360 frames | 14 s | 303 s | 197 s | 1,598 s (space-bunny: 313 s) |
| Iterations | 4 checks · 2 renders | 4 checks · 1 render | 6 builds · 2 renders | 4 builds · 1 render |
HyperFrames + Three.js
How
- 2,000 instanced glyph billboards; every position is a GLSL function of time.
- Each glyph is evaluated at t and t − 1/48 s and stretched along its own screen velocity (180° shutter).
- Per-glyph circle of confusion: grows, dims and melts into a bokeh disc.
- God rays: occlusion pass → 80-tap radial blur. UnrealBloom for the burst.
Friction
- Lint wants a
__timelinesentry even for a Three-only scene. - The ray quad was silently frustum-culled.
- No built-in DoF, motion blur or god rays: every look is code you write yourself.
Blender 5.2 · EEVEE
How
- Geometry Nodes instance 36 glyph meshes on 2,000 points; positions are nodes over Scene Time.
- Real camera DoF (f/1.2 rack), EEVEE motion blur on the instances, world volume haze.
- Spot light behind the bars scattering in the volume; compositor Glare (Bloom).
- Everything built from one Python script, headless.
Friction
- The factory reset freed the loaded font.
- The Mix node's vector output is index 1, not 2. Using 2 silently kept the swarm frozen.
- 5.x compositor: a node group with menu sockets.
- About 70 lines of Python to build the node graph.
EffectCraft 0.3.1
How
- 2,000 3D text layers, each with a position expression holding its own slots.
- Advanced 3D extruded bars; two-node camera keyed per frame.
- CC Light Rays centred on the disk via
toComp(); Glow and Exposure adjustment layers.
Bugs found
- Under the Advanced 3D renderer (needed for the extruded bars), camera DoF blurs every layer uniformly regardless of focus distance. Space-bunny showed it works under Classic 3D.
AutoOrientType.CAMERA_OR_POINT_OF_INTERESTthrows for layers.lookAt()works around it, but the first try mirrored the text.- CC Light Rays posterizes into rings at strength.
Tesseract 0.3.1
How
- No scene camera exists: layers are 2.5D postcards, and
positionZhas no perspective. A JavaScript pinhole projection runs inside every property script. - 2,000 text layers; position, scale, opacity, fill and blur radius all come from projected depth.
- Bars are flat billboards foreshortened with
rotationY. God rays: a radial-blurred occluder group.
Friction
- Building took ~17 min: applying a batch gets slower as the project grows (12 s → 225 s per 2,500 actions).
- Slowest render of the four by 5–100×. Motion blur shows as stepped ghost copies.
- A strict, well-validated action API, but small format rules each cost a round trip.
Same brief, different driver
space-bunny runs
opencode with opencode-go/space-bunny, given the same BRIEF.md and TOOL.md, running unattended with --auto. Three of the four finished. Blender was stopped after ~2 h, partway through its first full render.
| HyperFrames · Claude | HyperFrames · space-bunny | EffectCraft · Claude | EffectCraft · space-bunny | Tesseract · Claude | Tesseract · space-bunny | |
|---|---|---|---|---|---|---|
| Wall time to final | ~25 min | 48 min | ~45 min | 72 min | ~75 min | ~140 min |
| Full renders | 2 | 2 | 2 | 4 | 1 | 2 |
| Render, 360 frames | 14 s | 19 s | 197 s | 120 s | 1,598 s | 313 s |
| DoF | shader bokeh | thin-lens shader | ○ off (Advanced 3D bug) | ● real camera DoF (Classic 3D) | JS-projected blur | per-instance blur in one WGSL shader |
| Glyphs | 2,000 instances | 2,000 instances | 2,000 layers | 120 layers × 17 glyphs | 2,000 text layers | 2,000 shader instances |
| Visible defects | god rays faint | 34 bar runs off the top of frame at 7–9.5 s; title tiny | god rays faint; no haze | chart renders upside down; "34" and title mirrored | no god rays; stepped motion blur; square "disk" | word is particle-filled, not solid; god rays screen-space |
| Notes honest about defects? | yes | no: marks the chart done | yes | no: marks chart and labels done | yes | mostly: lists its limits |
HyperFrames + Three.js · space-bunny
The most dramatic swarm of any run: heavy motion-blur streaks and bright glints on the word. It caught a real bug in its own camera motion blur, which used the previous frame's matrix and smeared on random seeks. The framing on the tall bar was never checked.
EffectCraft · space-bunny
A smarter build than Claude's: 120 text sheets of 17 glyphs each, laid along the letter strokes, plus real camera DoF on the default renderer. But the bar chart hangs below its baseline and the labels read backwards.
Tesseract · space-bunny
The best idea of the round. Instead of thousands of layers it drew the whole 3D scene in one full-frame WGSL customShader: real perspective camera, 2,000 instances with per-glyph depth of field, lit 3D bars, and an occlusion-marched god ray. Native text layers for the labels are projected through the same camera. Its project is 1.9 MB against Claude's 204 MB, and it renders 5× faster than Claude's. Most of its time went into bisecting WGSL that failed silently as a flat frame. It ran nearly twice its time budget.
Round 1 brief · EXPOSURE (12 s, 2D motion + film finish)
Tesseract · space-bunny
Strips, top to bottom: Tesseract by space-bunny, HyperFrames by Claude, EffectCraft by Claude. This is Tesseract's home turf and it shows: every beat lands, built from native layers, effects and WGSL (halation, grade, grain, vignette in one finish adjustment). One miss: at 11.2 s the gate is still open when it should be closing. The notes are thorough, including a 65-check verifier script.
How each run went
Run narratives
Reconstructed from each agent's own commentary in its transcript, plus its notes. Open any run to read it.
SIGNAL · HyperFrames · Claude
- Read the Three.js adapter contract, then wrote the whole scene as one GLSL-driven instanced mesh: every glyph's path is a function of time in the vertex shader.
- Built depth of field per glyph (a circle of confusion grows and softens each sprite) and motion blur by evaluating each glyph at t and t − 1/48 s and stretching it along its own screen velocity.
- First snapshot: squares instead of bokeh, a light disk drowning the chart, bloom on everything, the chart cropped, the title off-frame, the burst whiting out the frame. Fixed in one pass.
- God rays were missing entirely: the full-screen ray quad was being frustum-culled. Fixed, then moved the light behind the bars; the rays still read as a soft glow.
- Rendered in 14 s.
SIGNAL · Blender · Claude
- Probed the Blender 5.2 API first (compositor now a node group with menu sockets; EEVEE renamed back).
- Wrote one Python script that builds everything: 36 glyph meshes instanced by Geometry Nodes on 2,000 points, positions as a node graph over Scene Time, a world volume, a DoF camera, a spot light for volumetric rays, and baked keys for the bars and camera.
- Hit the font-freed-by-factory-reset bug, then a silent one: wiring the Mix node's color output instead of its vector output left the swarm frozen in the field.
- Fixed the stray black flash sphere; checked stills, then rendered all 360 frames in 303 s.
SIGNAL · EffectCraft · Claude
- Probed whether 2,000 expression-driven 3D text layers were feasible (69 s to build, 266 ms per frame), and found Advanced 3D adds extrusion and the camera has DoF.
- Generated per-glyph slots with Blender, then wrote 2,000 position expressions and extruded bar layers.
- Hit three EffectCraft problems: auto-orient toward the camera throws for layers (worked around with
lookAt(), which first mirrored the text); DoF blurred everything regardless of focus under Advanced 3D (turned off); CC Light Rays posterized into rings. - Cut motion-blur samples to 8 (16 cost 6.7 s per frame), and rendered in 197 s.
SIGNAL · Tesseract · Claude
- Read the skill and the 800 KB schema; found 2.5D layers but no scene camera (
positionZhas no perspective). - Wrote a Python generator: a JavaScript pinhole camera inside every property script of 2,000 text layers, flat bars tilted with
rotationY, an occluder group for rays. - Six rejected batches taught the format: required empty dependencies, composition-unique effect IDs,
paramsas name/value pairs. - A single 22k-action batch stalled; chunked it and measured each chunk getting slower (12 → 225 s). The build took 17 minutes and made a 204 MB project.
- Patched blur on the square light and burst plus an early exposure lift, then exported: 1,598 s.
SIGNAL · HyperFrames · space-bunny
- Read the brief and docs, then wrote the composition shell and a separate
signal.jsscene. - First review: overexposed, an illegible word, sprites streaking into repeats, the chart reading as slabs. Rebalanced exposure and geometry scale.
- Found a real bug: camera motion blur used the previously rendered frame's matrix, so out-of-order seeks produced huge fake smears. Made it analytic.
- Fixed the burst (NDC vs UV coordinates put it in a corner), a too-wide ribbon, and a near-black frame on the 14.0 s crash.
- Shipped with the tall bar running off the top of frame, which its notes don't mention.
SIGNAL · EffectCraft · space-bunny
- Ran a long series of capability probes (36 probe and calibration scripts): DoF and motion blur confirmed working; CC Particle World renders nothing; repeater copies don't vary; per-character expressions evaluate once.
- Designed around the limits: 120 text sheets of 17 glyphs, each placed on measured letterform ink and rotated along the stroke.
- Spent several iterations on camera framing, concluding the camera ignores orientation and orbiting the chart rig instead.
- Fixed NaN positions from an unsubstituted variable, label opacity, a grey-rectangle burst, and collapse timing.
- Shipped with an upside-down chart and mirrored labels while marking them done.
SIGNAL · Blender · space-bunny
- Wrote six API probes and A/B diagnostics (DoF on/off, no volume, no motion blur, no compositor, linked vs unlinked collections).
- Rendered 45 check frames but never a full sequence; only one narration line in the whole transcript ("Now the real build script, with all the API fixes I learned from the probes").
- Stopped by me at about twice the 60-minute budget.
- Its session kept going in the opencode service after it was supposedly stopped. Its first full render was at frame 87 of 360 when I killed it.
SIGNAL · Tesseract · space-bunny
- Took a different route from Claude's: one full-frame WGSL shader drawing the camera, instances, bars and rays, with native text layers projected through the same camera.
- Spent most of the session bisecting shader failures that render as a flat frame (
mod, untyped float literals, a local namedtarget), and wroteprobe.pyandcheck_literals.pyto catch them. - Fixed a mirrored camera, dead-code label returns, and god rays that read as grey slabs; checked its JS and Python cameras against each other.
- Two full exports, the second after a fade fix; wrote NOTES.md with honest limitations.
EXPOSURE · Tesseract · space-bunny
- Probed the environment and wrote
build_exposure.pyto generate both the finished and clean documents. - Measured glyph metrics with its own probe tools, since point-text anchors are layer-local; that fixed a baseline sitting 110 px low.
- Found that blur clamps at a shape's own bounds, so it widened the leak shape to keep its gradient from plateauing.
- Wrote a 65-check verifier against the brief; all checks passed, though the gate is still open at 11.2 s.
Inputs
What each agent was told
The exact text each run started from. Open any of them to read it.
SIGNAL brief: round2/BRIEF.md (every SIGNAL run)
# SIGNAL — a 15-second 3D motion graphic A harder study than round 1. You are the motion designer. Build it with the assigned tool only (see "Rules"). This tests a real 3D camera, thousands of instances, data-driven animation, depth of field, motion blur, volumetric light, and audio sync. ## Inputs (in `fx-lab/round2/assets/`) - `beat.wav`: 15 s, 120 BPM; a beat every 0.5 s and a downbeat every 2 s. It builds into 3.0 s, and there's a crash at 14.0 s. - `beats.json`: the exact beat times, plus the key hits: `assemble` 3.0, `chart_start` 6.0, `burst` 14.0. - `commits.csv`: 12 days of real commit counts (`day,commits`). Use these values exactly. ## Spec - 1920×1080, 24 fps, exactly 15.0 s (360 frames), with `beat.wav` as the soundtrack. - Palette: deep blue-black `#05070d`, signal cyan `#5ce1ff`, white `#f4f7ff`, accent amber `#ffb000` (used once, on the peak). - Type: a geometric or grotesk sans (the tool's best option). | Time | Beat | |---|---| | 0–3 s | The camera drifts slowly forward through a dark 3D field of about 2,000 small glowing glyphs (random letters and digits) spread in depth. Real depth of field: a focus plane racks from near to far. Faint volumetric haze. | | 3.0 s (the downbeat) | The glyphs swarm and assemble into the word `SIGNAL`: each instance flies to a slot on the letterforms, staggered, with visible motion blur. The camera pushes in. The word has formed by about 4.5 s. | | 4.5–6 s | The word holds and pulses on each beat (a subtle emissive flash). | | 6.0–11.5 s | The word dissolves into a ribbon that becomes the baseline of a 3D bar chart. One bar rises per beat (12 bars on 12 beats, 6.0…11.5 s), with heights exactly proportional to `commits.csv`. The camera slowly orbits about 40°. A back light throws god rays through the bars. The tallest bar (34) turns amber and carries a value label. | | 11.5–14 s | Day labels and a title, `COMMITS / DAY`, appear. The camera pulls back and reveals the whole chart. | | 14.0 s (the crash) | Everything collapses into a single point and releases one bright light burst (bloom), then fades to black by 15.0 s. | Every hit lands on its beat, within one frame. ## Deliverables (in your run directory) - `out/final.mp4`: H.264 yuv420p plus AAC audio, 1920×1080, 24 fps, 15 s. - The source project or script that produced it (re-renderable). - `NOTES.md`, covering: - every beat, marked done, approximate, or impossible, with why; - the number of renders; - render time per frame; - workarounds; - what was precise, and what fought you. ## Rules - The motion, 3D, depth of field, motion blur, light, and bloom must come from the assigned tool. Use `ffmpeg` only to mux audio, transcode, or extract check frames. - Check your own work: extract frames at 1.5, 3.5, 4.6, 7.0, 9.5, 12.5, and 14.2 s and look at them before calling it done. - Budget: about 60 minutes and 20 full renders at most. If a beat is impossible, say so and move on.
EXPOSURE brief: BRIEF.md (every round 1 run)
# EXPOSURE — a 12-second motion study One idea, built separately in each tool, so we can compare how well an agent can control it. You are the motion designer. Build it with the assigned tool only (see "Rules"). ## Spec - 1920×1080, 24 fps, exactly 12.0 s (288 frames), no audio. - Palette: warm black `#0b0907`, tungsten `#ffb36b`, cream `#f3e6d0`, leak orange `#ff6a2a`, leak magenta `#d23a7a`. - Type: a heavy condensed or geometric sans (Futura Bold, Avenir Next Condensed Heavy, or the tool's closest). Title is all caps. | Time | Beat | |---|---| | 0.0–1.5 s | Black. 40–80 small, soft, dim dust motes drift slowly (particles). | | 1.5–3.0 s | A 2 px tungsten line draws from the center outward to the full width (expo-out ease) with a strong bloom/glow. | | 3.0–4.5 s | The line splits into two edges that open vertically like a projector gate, to a 2.39:1 letterbox (804 px tall). Inside is a warm radial gradient (tungsten center to warm black). | | 4.2–6.5 s | `EXPOSURE` (cream, ~220 px tall) builds letter by letter. Each letter starts 0.06 s after the previous one and goes from scale 1.15, blur 12 px, opacity 0 to scale 1, sharp, opaque. Over the beat the tracking tightens from wide to tight. | | 6.5–8.5 s | A soft orange→magenta light leak sweeps left to right (screen/add blend). The title gets chromatic aberration (RGB split) that peaks at ~6 px as the leak passes, then returns to 0. | | 8.5–10.5 s | The subtitle `twenty-four times a second` (small caps, wide tracking, ~44 px) fades in under the title, and a thin underline draws beneath it. | | 10.5–12.0 s | Everything fades except the gate, which closes back to the line. The line collapses to a center dot, and the dot fades to black. | **Finish (inside the tool, if it can):** a warm print grade, halation (a red-orange glow bleeding from the highlights), soft luminance-weighted grain (mostly luma, ~1.5 px clumps, strongest in the midtones and weakest in pure black), and a light vignette. The frame stays steady: no shake, gate weave, or flicker. ## Deliverables (in your run directory) - `out/final.mp4`: H.264, yuv420p, 1920×1080, 24 fps, 12 s. - `out/clean.mp4`: the same render without the film finish, if the tool allows it. - The source project or script that produced it (so it can be re-rendered). - `NOTES.md`, covering: - every beat, marked done, approximate, or impossible in this tool, with why; - the number of render iterations; - workarounds; - the tool's controllability: what was precise, and what fought you. ## Rules - Motion, VFX, and the finish must come from the assigned tool. Use `ffmpeg` only to transcode or encode the tool's output (no filters beyond scale/format), plus extracting frames to check your work. - Check your own work: extract frames at 1, 2.2, 3.8, 5.5, 7.4, 9.5, and 11.2 s and look at them before calling it done. - Budget: at most about 45 minutes and 20 full renders. If a beat is impossible, say so and move on.
space-bunny wrapper prompt, SIGNAL (opencode run --auto)
Shell variables expand to the brief, TOOL.md and run-directory paths.
You are a motion designer. Read $R/BRIEF.md and $R/runs/$t/TOOL.md, then build the study in this directory ($d) and write every deliverable. Work autonomously to the end; don't ask questions.
space-bunny wrapper prompt, EXPOSURE
You are a motion designer. Read $L/BRIEF.md and $L/runs/$t/TOOL.md, then build the study in this directory ($d) and write every deliverable. Work autonomously to the end; don't ask questions.
TOOL.md: hyperframes (SIGNAL)
# Tool: HyperFrames (HTML compositions rendered to video) CLI: `bunx hyperframes` (v0.8.127). Read the skills first: ~/.agents/skills/hyperframes/SKILL.md, hyperframes-core, then hyperframes-animation (its Three.js adapter is the route to real 3D), and the threejs-* skills in ~/.agents/skills as needed (for example threejs-bloom, threejs-camera-direction). Audio goes in the composition as an `<audio>` element (see hyperframes-core references/variables-and-media.md).
TOOL.md: blender (SIGNAL)
# Tool: Blender 5.2 LTS (native Apple Silicon): Geometry Nodes, EEVEE/Cycles, compositor Headless: `/Applications/Blender.app/Contents/MacOS/Blender -b -P build.py` builds a scene from a Python script; `-b study.blend -a` renders the animation (or set the output path and `bpy.ops.render.render(animation=True)`). Alternatively use the blenderbox CLI (`~/.local/bin/blenderbox --help`; read ~/.agents/skills/blenderbox/references/workflow.md). Use Geometry Nodes for instancing, the camera's depth of field, EEVEE motion blur and volumetrics (or Cycles), and compositor Glare for bloom. Audio: mux `beat.wav` with ffmpeg after rendering, or use the VSE.
TOOL.md: effectcraft (SIGNAL)
# Tool: EffectCraft 0.3.1 (open-source After Effects clone in Rust)
CLI: fx-lab/.tools/effectcraft/effectcraft-cli (run with no args for usage; read fx-lab/.tools/effectcraft/README.md). The fastest path is `effectcraft-cli script build.jsx --save-as study.ecproj` with the After Effects-style object model (3D layers, cameras, lights, CC Particle World, expressions), plus `render-frame` for checks and `render` for the movie. Known quirks from round 1: no function hoisting or String.substr in scripts; gradient colors must be set through `effectcraft-cli set ... colors '{"colors":[[0,"#hex"],...]}'`; keyframes snap to whole frames.
TOOL.md: tesseract (SIGNAL)
# Tool: Tesseract 0.3.1 by Mirage (native layer/composition engine, "the creative suite built for agents") CLI: fx-lab/.tools/tesseract/bin/tesseract-0.3.1-darwin-arm64/bin/tsrct (run `--help`; telemetry is disabled; don't re-enable it). Skills: read fx-lab/.tools/tesseract/skills/tesseract/skills/tesseract-motion/SKILL.md first and follow its references (installation, local-operation, native-authoring, motion-design, fx-authoring, motion, custom-shader). Pull exact fields from `tsrct project schema` and `tsrct project schema --document`. When the skill mentions an attribution variable, use TESSERACT_HOST=claude-code-lab. Fonts must be imported from a TTF/OTF file; Jost is at fx-lab/round2/runs/blender/claude/Jost.ttf.
TOOL.md: tesseract (EXPOSURE)
# Tool: Tesseract 0.3.1 by Mirage (native layer/composition engine, "the creative suite built for agents") CLI: fx-lab/.tools/tesseract/bin/tesseract-0.3.1-darwin-arm64/bin/tsrct (run `--help`; telemetry is disabled; don't re-enable it). Skills: read fx-lab/.tools/tesseract/skills/tesseract/skills/tesseract-motion/SKILL.md first and follow its references (installation, local-operation, native-authoring, motion-design, fx-authoring, motion, custom-shader). Pull exact fields from `tsrct project schema` and `tsrct project schema --document`. When the skill mentions an attribution variable, use TESSERACT_HOST=claude-code-lab. Fonts must be imported from a TTF/OTF file; Jost is at fx-lab/round2/runs/blender/claude/Jost.ttf.
Claude runs
No separate prompt. Claude (Opus 5.5) wrote the briefs and TOOL.md files above and then built each study itself from them, so it also knew the other tools' results as it went: a head start the space-bunny runs didn't have.
The take
Blender looks the most photographic and is the one to beat for 3D and VFX. HyperFrames is 20× faster and fully controllable, but its effects are things you code yourself. EffectCraft speaks After Effects fluently, but in 3D it loses depth of field and fog. All three faked the god rays. Tesseract, which bills itself as the creative suite built for agents, had no camera and no instancing, and it rendered 100× slower than HyperFrames. But space-bunny found the way in: one WGSL shader as the scene, which turned the slowest tool into a credible 3D one. Tesseract is also strong in 2D (space-bunny's EXPOSURE). space-bunny finished 3 of 4 SIGNAL runs, took about 2× longer than Claude, and in two of them its notes called defects fixed when they weren't.