-
Notifications
You must be signed in to change notification settings - Fork 0
Troubleshooting
zhanglinghao edited this page Oct 3, 2026
·
4 revisions
English · 中文
Symptom, cause and fix for problems the repo documents. Start with bin/vh doctor. If you installed a few days ago, run git pull first: many fixes (Linux, audio, rendering) are on main but not yet in a tagged release.
| Symptom | Cause | Fix |
|---|---|---|
install.sh stops with "✗ node is required" (or ffmpeg, or git) |
a prerequisite is missing | Install git, Node.js ≥ 22 and FFmpeg, then re-run |
The installer prints skip <repo> (clone failed)
|
an upstream reference repo was unreachable; it is skipped rather than aborting the install | Re-run references/fetch.sh later, or references/fetch.sh <dir> for a single repo |
npm ci in styles/_swatch fails on registry.npmmirror.com
|
an older lockfile pinned that mirror |
git pull (fixed), then cd styles/_swatch && npm ci
|
doctor shows "✗ chrome" |
no Chrome or Chromium found | macOS: install Google Chrome. Linux: sudo apt install chromium or npx playwright install chromium. Or set CHROME_PATH
|
doctor shows "! no WebGL with the GPU flags" |
headless Chrome has no usable GPU | Render with node render.mjs --soft-gl (software WebGL). If doctor reports no WebGL with --soft-gl either, WebGL scenes won't render on that machine |
doctor shows "! AGENTS.md out of sync" |
CLAUDE.md changed without regenerating AGENTS.md
|
bin/vh sync-agents |
| Downloads (npm, PyPI, Hugging Face, Chrome) are slow or fail | the first runs fetch from servers abroad | Mirrors for each are on the China network page; for GitHub itself, install with --no-refs and fetch references one at a time |
| Asking for a video in another folder doesn't reach the harness | the agent hasn't reloaded its skills | Restart Claude Code or Codex after bin/vh install-skill
|
Linux: bin/vh new … --effort studio still writes standard; swatch renders die after rendering; the determinism check fails on md5
|
older versions used macOS-only sed -i '', stat -f and md5 -q
|
git pull: fixed on main
|
bin/vh setup -h starts the whole install, bin/vh sync-agents -h rewrites AGENTS.md, or a command takes -h as a file name |
older versions passed -h and --help on to the command |
git pull: bin/vh <command> -h now prints that command's usage and exits without running it |
| Symptom | Cause | Fix |
|---|---|---|
| A HyperFrames render hangs with an empty log and no error | a CDN import (GSAP, three.js) with no network when the page loads | Install dependencies into the project (npm i -D --save-exact) and point the script tag or importmap at node_modules. bin/vh hf-init does this for GSAP and warns about other CDN links. Wrap long renders in a watchdog |
A HyperFrames render sits at 5 % "Compiling composition", or snapshot and check print nothing |
a font in the stack has no @font-face, so HyperFrames asks Google Fonts for it on every run (cached font files don't stop the request), and the request hangs where Google Fonts is blocked. Names in HyperFrames' alias list (Arial, Helvetica Neue, Menlo …) without an @font-face are swapped for Inter or JetBrains Mono and fetched too |
Declare every font with @font-face and src: local(…); projects scaffolded by bin/vh hf-init since #35 already do. To self-host a web font through npm, see the 国内网络 section of README.zh-CN.md
|
The first hyperframes render shows only "Checking browser…" for a long time |
it is downloading HyperFrames' own chrome-headless-shell (about 100 MB, 200 MB unpacked, into ~/.cache/hyperframes/chrome) and a non-interactive shell prints no progress; having Google Chrome installed doesn't skip it |
Run npx hyperframes browser ensure once in a HyperFrames project before the first render: it says what it fetches and how big. If it can't download, see the mirror settings above |
bin/vh new prints "hyperframes init failed (offline?)" |
the scaffold is fetched with npx | When online, re-run bin/vh hf-init <project> with portrait, landscape or square
|
HyperFrames skills appear in ~/.claude/skills
|
its init and skills update install globally |
export HYPERFRAMES_SKIP_SKILLS=1 before any npx hyperframes command, and never run npx hyperframes skills update. bin/vh hf-init sets it for you |
font-weight: 800 is in the spec, but PingFang SC renders no bolder than 600 |
macOS's PingFang SC has six weights (100–600); 700, 800 and 900 fall back to 600, pixel-identical, with no error | Write 600 and note it in STYLE.md. For a real 800 bring a font that has it (Noto Sans SC), or use a system font with heavier weights (Songti SC has 900) |
--format png-sequence frames have a transparent background, and the psnr command reads about 1.25 dB high |
the PNGs are RGBA and the html, body and root backgrounds aren't painted (alpha 0), so an mp4 made from them has a black background; the alpha channel counts as an error-free fourth component in psnr
|
Keep it in mind for determinism checks; a per-pair loop with format=rgb24 is in playbook/02
|
hyperframes snapshot sends the frames to Gemini |
with GEMINI_API_KEY (or GOOGLE_API_KEY) in the environment, snapshot describes every frame by default, so the frames leave your machine and are billed |
Always pass --describe false; the repo's docs and hints already do. check isn't affected |
| Every HyperFrames render is suddenly about 3× slower | HyperFrames switched this machine to a slower capture path | Set HF_DE_PARALLEL_ROUTER=true
|
| A stretch of frames is stuck on the t=0 picture, yet the render "succeeded" | an exception inside renderAt, e.g. a const read before it is declared |
Compare every frame with frame 0 (PSNR), or keep a time readout on screen in drafts; fix the exception |
| The first 1–2 frames of each worker's chunk are empty | frames captured before an async build finished | Register a ready promise synchronously in a classic <script> and resolve it after the first frame is drawn |
| Three.js video screens render black | the VideoTexture isn't updated |
Set texture.needsUpdate = true every frame; seek the video by t and wait for the seek |
| Text goes soft after a camera push-in |
will-change: transform rasterized it at the small size |
Remove will-change from elements that get zoomed, or lay them out at the zoomed size |
| Monospace text renders proportional in the MP4, though snapshots looked right |
ui-monospace, monospace passes lint but not the renderer |
Declare the font with @font-face and src: local("SF Mono"), local("Menlo"); judge fonts on rendered frames |
| The picture is cut short, or the last frames go missing, when adding audio | ffmpeg's -shortest
|
Mux with bin/vh mux, which pads or trims the audio to the video |
| Saturated colours look different in the browser or on a phone than in ffmpeg or an editor | the mp4 has no colour tags, or BT.601 tags: the old hand-drawn engine wrote bt470bg, which browsers read wrongly (24–40 levels off on saturated colours) while ffmpeg reads it right |
Encode limited-range BT.709 and write all four tags; bin/vh check flags anything else. The ffprobe check and the ffmpeg command are in playbook/02
|
| You review an old render by mistake | a stale output file | Delete old outputs before rendering, or check the file's modification time |
bin/vh check flags black, frozen or silent stretches |
dark backgrounds, deliberate holds and silent films trigger it by design | Look at each hit and decide |
| Symptom | Cause | Fix |
|---|---|---|
| The same frame differs between runs |
Math.random(), Date.now(), CSS transitions or @keyframes, or state carried between frames |
Use a seeded hash(i) and compute everything from t |
With --soft-gl, frames differ on every launch (about 39 dB) |
Chrome's canvas readback noise | Fixed on main; run git pull
|
| Two renders compared as MP4s look non-deterministic (about 42 dB) | x264 amplifies tiny GPU rasterization differences | Compare lossless PNG frames: pixel-identical, or ≥ 45 dB PSNR with no visible difference, passes |
| Two renders of the same HyperFrames project differ in a few text-edge pixels, or an encode flips between two sizes at a size cap | GPU text rasterisation differs from one Chrome process to the next, and x264 with frame threads isn't repeatable under a bitrate cap |
hyperframes render --no-browser-gpu (CPU, slower) makes the pixels repeatable, and -threads 1 the x264 stream. The swatch renderer does both: research note 03 has the measurements |
| Still objects jitter in a hand-drawn scene | a moving object shifted the shared random stream | Give each element its own boilSeed()
|
| Symptom | Cause | Fix |
|---|---|---|
Chinese characters on a canvas are missing or in a system font, although document.fonts.ready resolved |
CJK web fonts arrive as subsets, fetched only when a character is first drawn | Call document.fonts.load(…) once per weight with every character the film uses, and fail loudly on a rejection, an empty result or a timeout |
| The first frame uses a fallback font and the layout jumps | the frame was captured before web fonts loaded |
await document.fonts.ready before the first capture |
| HyperFrames lint rejects a Chinese font | Noto Sans SC isn't in its automatic font list | Add a Google Fonts <link>, or put the font file in assets/ with an @font-face rule |
From the smoke tests and the intro film's opening; more in engines/blender.md.
| Symptom | Cause | Fix |
|---|---|---|
| A Blender render prints nothing for about two minutes at the start, and a watchdog kills it | the first Metal render compiles its kernels silently (about 110 s) | Allow a longer silence for Blender (the swatch renderer allows 300 s) |
| Under the sandbox every Metal frame takes 13 s instead of 2 s | Blender can't keep its compiled shaders in its folder of the per-user cache | Allow writes to $(getconf DARWIN_USER_CACHE_DIR)org.blenderfoundation.blender
|
| Rendering frame by frame in one process, an object goes black for a stretch of frames but renders fine on its own |
render.use_persistent_data carries state from frame to frame |
Turn it off when a script changes the scene before every frame, or compare frames re-rendered out of order |
| A long render stops halfway when run as an agent's background task | Claude Code ends background tasks after 30 minutes | Render in chunks of fresh processes that skip finished frames, detached with nohup caffeinate -i … & disown, as showcase/04-intro-film/tools/bl_render.sh does |
| Stars smear into blotches | the OIDN denoiser | Turn denoising off for star fields and rely on samples |
| Stacked glass cards render as black blocks | rays run out of transmission bounces | Drop the glass on cards that don't need it |
| The camera twists and jitters when it looks almost straight down |
to_track_quat is unstable near vertical |
Build the rotation from your own forward and up vectors, and keep consecutive quaternions on the same side (negate when q.dot(prev) < 0) |
| Text in Blender comes out in the wrong weight |
bpy.data.fonts.load() reads only the first face of a .ttc
|
Write the face you need out as its own font file (styles/_swatch/blender_prep.py does it with fontTools) |
| Symptom | Cause | Fix |
|---|---|---|
A short English line from qwen mumbles on for seconds |
Qwen3-TTS 0.6B run-on, worst with the Ryan voice | Aiden is now the default. Add --align gemini to flag such lines, then re-run them, use a 1.7B model (QWEN_TTS_MODEL) or gemini
|
say or qwen fails on Linux |
say is macOS-only; qwen needs Apple Silicon |
Use edge or a cloud provider |
--align gemini pauses for about a minute, then carries on |
Gemini Tier 1 allows 10 transcriptions a minute (HTTP 429) | Nothing to do: it waits as the API asks and retries up to 5 times. A 429 asking for more than 90 s is a daily quota and fails at once |
--align gemini stopped halfway (a daily quota, the network) |
a transcription failed for good | The take is kept and the command exits 1 with the reason. Run the same command with --resume: it redoes only what's missing and doesn't pay twice for lines that already succeeded. |
| "set GEMINI_API_KEY" | the key isn't in the environment |
export GEMINI_API_KEY=… in your shell, never in a file in the repo |
bin/vh tts <p> --provider edge fails with "No such file or directory: 'edge-tts'" |
older bin/vh chose dependencies only from the positional provider |
git pull (fixed), or write the provider positionally: bin/vh tts <p> edge
|
bin/vh qa fails on digital silence mid-film |
a dramatic stop made of true silence | Keep a bed (sub or pad), close the filter, drop the drums, swell into the next hit |
bin/vh qa reports pumping, or the music drops out between lines |
the ducker keyed on every SFX, or a high duck_ratio
|
Use the defaults (duck=voice, duck_ratio=1.6); with duck=on keep the ratio at 2–3. Pass --voice voiceover.wav to qa so the designed dip under narration isn't counted |
bin/vh qa prints a mix report with LOW, HIGH, OVER-VOICE, MASKS-VOICE or BURIED
|
a profile= mix has a line, a sound effect or a word outside its range; BURIED means a cue is found but can't be heard |
Read the "why" column first. Fixes by flag are in playbook/04 ("怎么读混音报告"): re-record or ease the score under a quiet line, move or reclassify the effect (role), raise gain_db, or add a 1–4 kHz layer to a low-frequency hit instead of gain |
| The AAC-encoded file's true peak is over −1.5 dBTP | an AAC encode adds to the true peak, up to about +1.4 dB at 128k on dense music | Measure the encoded file with ffmpeg -i final.mp4 -af ebur128=peak=true -f null - and mix again with a lower tp=; the swatch renderer does this by itself |
| A browser waits for the whole video to download before it plays | the index (moov) sits at the end of the file |
Re-mux with bin/vh mux, which now writes it first |
| A player switches a subtitle track on over burned-in captions | MP4 always enables the first subtitle track when none is marked default | bin/vh mux --subs-off |
bin/vh readcheck fails burned-in captions that repeat the narration |
they were checked by the on-screen rule, which assumes slower reading | Mark them data-read="subtitle", on the clip or on the scene around it, and they are checked at subtitle reading speed |
| A caption flashes by in under 1.8 s | the spoken line is short | Run bin/vh captions again: it lengthens short cues into the silence after them, up to the next caption or the end of the picture |
bin/vh qa lists clicks |
a machine can't tell a designed sharp attack from a fault | These are warnings: listen to the 10 worst by ear |
bin/vh qa warns that a sound repeats |
the same sound came back three or more times in a row: a pinned variant, or the same file |
Unpin the variant, rotate two or three takes, or use another transition family; bin/vh sfx audition plays the options |
bin/vh mix reports many limiter peaks |
SFX or voice peaks are too hot | Lower sfx_db instead of leaning on the limiter |
Not listed here? Open an issue with the command, its output and the result of bin/vh doctor.