html2video
Skill13 starsChanged 3 months agoMITView on GitHub
Turn a web article URL into a polished 1920x1080 edited video (MP4) — code-driven editing with Remotion, not AI video generation. Use this whenever the user wants an article, blog post, research page or changelog made into a video, or says 文章转视频, 网页转视频, 把这篇做成视频, article-to-video, url-to-video, or asks for an explainer or montage cut of written content. It harvests the page's real structure, full-resolution figures and design tokens, mines the content for what matters (including the caveats most tools skip), commits to a reviewable storyboard.json, then renders through a hardened Chinese-typography shot library. Has a 口播 (spoken) mode — ask for 口播, 配音稿, a narration script or voice-over and it re-times the cut to speech and writes a recordable script with timecodes. Also use it to re-render or retune an existing storyboard.json, adjust crops or pacing, or pull stills.
1 other project uses a version of this.
Adopted by
Most recently taken up by
- johnson7788/collect_skillstheir own version, first seen Apr 29, 2026
Other versions1 project uses a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 1
- johnson7788/collect_skills★ 1same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
The file
Show its rules and text
We split this skill into the rules it gives an agent
14 rules. About 0 are common, 5 are distinctive, and 9 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈5
Distinctive · estimate
This author's own choices. Worth a look.
9
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 35
All paths below are relative to this skill's directory.
- Very specific: line 47
Read harvest.json before doing anything else.
- Very specific: line 49
figures[].caption, and itscaptionSource: When a page really uses <figcaption>, each caption is a finding compressed into one sentence … - Very specific: line 59
When it is proximity or none, open the image and read the finding off the chart — its own embedded title is usually the best source.
- Distinctive: line 68
If the fetch looks thin, retry attached to a logged-in Chrome:
- Distinctive: line 88
Write narrative headlines.
- Distinctive: line 105
Assets are local: No URLs anywhere; the render never waits on the network.
- Very specific: line 134
When the user asks for 口播 / a spoken version / a narration script, set audio.mode: "voice" and give every scene a narration line.
- Very specific: line 148
Read references/narration.md before writing any of it — it also covers trimming on-screen text, spoken syntax, and the --sps speech-rate …
- Very specific: line 156
Use
--concurrency 3or lower: if a render dies with Visited "http://localhost:3001/index.html" but got no response. - Distinctive: line 165
Do not re-render the whole video to check a change.
- Very specific: line 182
When a figure fights you, the answer is usually a different slot or fit, not a smaller font.
- Distinctive: line 222
Read it before changing any font size.
- Very specific: line 241
No music ships with this skill, because shipping music means shipping a licence.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
html2video
Produce a 1920x1080 H.264 video from a web article. Chinese output by default; English sources get translated during the reading stage.
The hard part is not rendering. It is deciding what deserves to be on screen — and the thing that separates this from a slideshow is treating a page's figures and figure captions as primary material rather than decoration.
The pipeline
HARVEST ──→ READ ──→ STORYBOARD ──→ RENDER
scripts you storyboard.json libraryFour stages, and the middle two are where the quality comes from. HARVEST and RENDER are deterministic and already written; READ and STORYBOARD are editorial judgement, which is your job.
| Script | Does |
scripts/harvest.py | URL to structured content, full-resolution figures, design tokens |
scripts/render.mjs | storyboard.json to MP4 (stages assets, subsets fonts, validates, renders) |
scripts/build_fonts.mjs | subsets the CJK faces to this board's characters (called by render.mjs) |
scripts/sync.sh | copies the engine from assets/template/ into the runtime studio |
All paths below are relative to this skill's directory. The render engine lives
in assets/template/ and runs from ~/.cache/html2video/studio/, which is where
node_modules is kept so it survives between runs.
1. HARVEST
python3 scripts/harvest.py --url "https://example.com/article" --out ./work/myrunProduces work/myrun/harvest.json plus work/myrun/media/fig-NN.png.
Read harvest.json before doing anything else. Look at, in this order:
figures[].caption, and itscaptionSource. When a page really uses<figcaption>, each caption is a finding compressed into one sentence with its illustration already attached — the densest editorial material available, and the shortest path to a good video.But check
captionSourcebefore trusting one.figcaptionis authoritative;proximitymeans the text merely followed the image and may belong to a different figure;nonemeans the page gave nothing. Many sites (Cloudflare's blog, for one) wrap images in<figure>with no caption at all.When it is
proximityornone, open the image and read the finding off the chart — its own embedded title is usually the best source. Re-author that as a Chinese headline, and put the English original inasset.sourceCaption.figures[].intrinsic— how many pixels you have to spend. A figure at 2688px can be pushed into; one at 1590px cannot fill a 1920 frame at all.sections— the article's real spine, with nav noise already dropped.tokens— the site's actual colours and fonts, from a real browser.cssVarsoften contains the brand palette by name.
If the fetch looks thin, retry attached to a logged-in Chrome:
python3 scripts/harvest.py --url URL --out DIR --cdp-url http://localhost:92222. READ
This stage has no script because it is the judgement. Load references/editorial.md and work through it. In short:
- state the core message in 40 Chinese characters or fewer
- pick 3 to 5 supporting points and order them by audience relevance, not by where they appear in the article
- triage everything else: keep / simplify / visualise / omit
- go looking for what the article half-buries — the limitation it admits, the number that contradicts expectation, the one example that makes it click. These are the moments other tools skip, and they are why a video is worth watching rather than skimming.
Write narrative headlines. "26% 的题目它知道自己在被测" beats "评测结果".
3. STORYBOARD
Author work/myrun/storyboard.json. The schema, every field's meaning, and a
complete worked example are in
references/storyboard.md. Shot-by-shot composition
and motion specs are in references/shots.md.
Three rules that the schema enforces, so it is cheaper to follow them than to fight them:
- Never author frames. Scene length is a dimensionless
weight; the solver converts weights to frames while respecting a reading floor computed from your text. Everything time-like inside a scene is normalised0..1. - Crops are normalised rects in source-image space, and a crop that would need more pixels than the image has is rejected before rendering.
- Assets are local. No URLs anywhere; the render never waits on the network.
Then validate, which is much faster than rendering:
node scripts/render.mjs --storyboard ./work/myrun --still 0The validation report prints resolved scene durations, pixel headroom per asset, and character budgets. Fix everything it flags before rendering.
4. RENDER
# full video
node scripts/render.mjs --storyboard ./work/myrun --out ./myrun.mp4
# one frame, for checking a layout
node scripts/render.mjs --storyboard ./work/myrun --still 950
# interactive preview
node scripts/render.mjs --storyboard ./work/myrun --studio
# 口播 mode: script only, no render
node scripts/render.mjs --storyboard ./work/myrun --script口播 mode
When the user asks for 口播 / a spoken version / a narration script, set
audio.mode: "voice" and give every scene a narration line. That does three
things, and the third is why this is a mode rather than an extra document:
- writes
narration.md— a recordable script with timecodes, what is on screen at each moment, and a per-scene duration budget - re-times the cut to speech rather than to reading speed, so the script and the video actually line up
- relaxes the reading floor, since the viewer is listening rather than reading
The video renders silent — you record over it. No TTS, no API key.
The single rule that matters: narration must not read the on-screen text aloud.
The screen says what it is; the narration says why, so what, and where it came from.
Read references/narration.md before writing any of it —
it also covers trimming on-screen text, spoken syntax, and the --sps speech-rate
flag.
render.mjs stages assets, wraps props, subsets fonts, validates and renders in
one step. Use it rather than calling remotion directly — see the props note in
references/pitfalls.md for why.
Use --concurrency 3 or lower if a render dies with Visited
"http://localhost:3001/index.html" but got no response. Measured on this
machine: 8 parallel tabs against the system Chrome fails that way, 3 is stable.
Stills always work because they run a single tab.
Iterating on the visuals
This is the real work, and there is no automated substitute for looking at frames.
Do not re-render the whole video to check a change. Pull stills at the middle of the scenes you touched:
node scripts/render.mjs --storyboard ./work/myrun --still 520 # a few seconds eachThe validation report prints each scene's start in frames and its duration in
seconds, so mid-scene is from + duration * fps / 2. Look for, specifically:
- text landing on top of a figure, or on a busy part of one
- a headline competing with type that is already inside the figure
- a crop that cuts a word, a chart title, or an axis label — for a chart this is
the most common problem, and the fix is
"fit": "contain" - rules and dividers that have vanished (anything under 2px does, in video)
- Chinese lines breaking mid-word — fix with an explicit
\nin the text
When a figure fights you, the answer is usually a different slot or fit, not a
smaller font. A chart or schematic wants "fit": "contain" so nothing is cropped;
a wide schematic wants band; a screenshot wants inset; a photograph wants
full with cover and a push-in.
Watch the overall shape too: if every figure ends up contain + hold, the middle
of the video will read as slides. Give at least one figure that has the pixels for
it a gentle push-in.
Constraints that are not negotiable
These come from Remotion's rendering model, and violating them produces videos that look fine in preview and break in the render:
- No CSS animations, transitions, or Tailwind
animate-*. Frames render across parallel browser tabs that share no animation state, so anything not driven byuseCurrentFrame()flickers. Animate withinterpolate(). interpolate()returns a number, never a string. Wrap it in a template literal fortranslate/rotate. Andtranslateneeds the two-value form: a bare number is translateX only.<Audio volume={(f) => …}>receives audio-local frames, not composition frames. The music bed is mounted at frame 0 outside any Sequence so the two coincide; keep it that way.- A
cutis the absence of a transition, not a zero-frame one.
The full list, with the reasoning and the failure each rule prevents, is in
references/pitfalls.md. Read it before editing the
engine in assets/template/src/.
Typography
Chinese in Remotion has one large trap: routing CJK through
@remotion/google-fonts means about 101 unicode-range subsets times 9 weights,
each its own FontFace and its own render-blocking delayRender() — hundreds of
network fetches per frame tab. This skill instead subsets two variable faces
locally with pyftsubset to exactly the characters the storyboard uses: about
310 codepoints becomes ~125KB total, versus ~36MB, with zero network at render.
The type scale, safe area, the exact character-per-line budgets and the CJK line-breaking rules are in references/cjk-type.md. Read it before changing any font size.
Requirements
Already present on this machine; listed so a failure is diagnosable.
| Needs | For | Check |
bun | runs baoyu-fetch during harvest | bun -v |
agent-browser | design tokens via getComputedStyle | agent-browser --version |
pyftsubset with brotli | font subsetting | pyftsubset --help |
sips | image dimensions (macOS built-in) | — |
| Node 20+, Chrome | rendering | node -v |
First run downloads two Noto CJK variable fonts (~37MB total) into
~/.cache/html2video/fonts/ and caches them permanently. If that download fails
the skill falls back to a macOS system font and says so — text metrics then vary
between machines, which breaks reproducible line breaking.
No music ships with this skill, because shipping music means shipping a licence.
Drop a file into the harvest directory and set audio.bed to its name; without
one the video renders silent.
Resources
- references/editorial.md — the READ stage: salience mining, triage, narrative arcs, banned phrasing
- references/storyboard.md — schema field by field, plus a full worked example from a real article
- references/shots.md — the ten shots: what each is for, its composition, its motion
- references/cjk-type.md — type scale, safe area, character budgets, CJK line breaking, font subsetting
- references/narration.md — 口播 mode: writing a script people can record, trimming on-screen text, how speech length is estimated
- references/pitfalls.md — every constraint and hard-won gotcha, with the failure it prevents
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

