Crayon storybook skill

Illustration · not a resultFrom the edenfunf/reelmimic README — shows the whole collection, not only this skill.source ↗
Make warm children's picture-book animations (MP4) drawn in crayon, coloured pencil and pastel on cream drawing paper — waxy crayon scribble fills broken by paper tooth, coloured-pencil outlines, slow 8 fps line boil, page-turn transitions, picture-book page framing and hand-lettered CJK storybook captions.
by edenfunf·MIT license·★ 3,630 Stars on the repo·GitHub ↗
npx degit edenfunf/reelmimic/.claude/skills/crayon-storybook#main ~/.claude/skills/crayon-storybookChecked ·commit main

Illustration · not a resultImages 1–4: From the edenfunf/reelmimic README — shows the whole collection, not only this skill.view on GitHub
Illustration · not a resultImage 5: From the edenfunf/reelmimic README — shows the whole collection, not only this skill.view on GitHub
Files of Crayon storybook
Show the full text81 lines
Crayon storybook
A drawing engine for ReelMimic. Every frame is a pure function of time t: studio.html loads p5.js + p5.brush, the
crayon look library, the cast and your scene files; each shot draws the whole 1920×1080 frame; render.mjs renders
contact sheets, strips and crops for review, or all frames in parallel, and ffmpeg encodes the MP4.
Provenance. The runtime (template/src/core.js, timeline.js, render.mjs, studio.html, the render contract and
helpers: paint, inkLine, camera, shots, glow, jump/take/spring, boilSeed…) derives from the
painted-animation skill (ClaudeAnimationBase, MIT © John Heibel / tuzhechen2005 — see template/LICENSE). The
look (crayon.js), the characters (cast.js), the page turn, the page finish and the captions are this engine's own.
Workflow
- Scaffold —
bash .claude/skills/crayon-storybook/scripts/new_project.sh <project-dir>(add--keep-demoto keep the example). It copies the template, runsnpm install(which also writes the patched p5.brush build, see gotchas) and checks node / ffmpeg / Chrome. Then read GUIDE.md in full and look atdocs/demo-sheet.jpganddocs/cast.jpgwith Read. - Storyboard in writing: shots, reads (what the viewer must understand, when), transitions, captions.
- Build —
src/config.js(duration,bpm,fps: 24); one IIFE file per shot/scene insrc/scenes/, ending withshots([...]); add its<script>tostudio.html. Captions:captions([[t0, t1, '文字', { pos }]])once. - Look, fix, look again (from the project dir):
Open every image with Read. At least a sheet per shot, a strip per key action and per transition, a crop per face.node render.mjs --sheet=0.5,1.5,2.5,3.5 --cols=4 --w=480 --out=out/check/a.jpg node render.mjs --strip=2.0:2.6 --cols=6 --w=320 --out=out/check/strip.jpg node render.mjs --sheet=2.3 --cols=1 --crop=600,200,900,700 --w=900 --out=out/check/face.jpg - Render —
node render.mjs --frames --workers=4 && node render.mjs --encode --out=out/video.mp4 [--audio=…].
API in one screen
| area | calls |
|---|---|
| fills | `colorIn(pts, { col, fill: 'scribble' |
| lines | crayonLine, pencilLine, pastelLine, sketchLine, inkLine(pts, sw, col, brush) — brushes crayon, crayonFine, cpen, pastel, chalk, graphite |
| shapes | ellPts, rectPts, rrPts, starPts, heartPts, through, ribbon (core) |
| scenery | sky, sun, cloud, hill/hillY, ground, tufts, tree, flower, house, paperWash |
| props | apple, leafProp, starProp, heartProp, balloon; emotes emote(kind, x, y, s, k, age) |
| cast | lulu(x, y, u, o), kuri(x, y, u, o) → { head, top, handL, handR, feet }; feel, emotions, walkCycle, pose |
| motion | seg, kf, ease*, backOut, spring, ring, jump, take, stroll, arcPt, onTwos, pulse, shakeXY |
| camera | camBegin(cx, cy, zoom, rot) … camEnd(), toScreen |
| staging | shots([[t0, fn], [t1, fn, { turn: 1.0 }]]) (page turn), bookPage(k), vignette(k), tooth(k), crayonWipe(p), iris, flash, glow |
| text | captions(list) (storybook captions via window.overlayHook), letter() for words inside the picture |
Rules that matter most
- Crayon medium only. Colour shapes in with
colorIn(pale base + crayon scribble/hatch + coloured-pencil outline); lines with the crayon/pencil brushes. No plain p5rect/ellipse/fill, no gradients, no 3D. Turns go through the drawn key views (front,q,side,back). - Pure functions of
t. NoMath.random(), no state between frames.hash()/rnd(key)for stable randomness;jit()only for boil. CallboilSeed(key)before each separate background element/prop. - Characters come from
cast.jsonly. Scenes pass poses (aR,bR,view,walk,dy,sq,hair,holdR…) — they never draw a character's parts themselves. New characters go incast.js(or one file each). - Big and legible. Lulu u ≈ 30–45 in medium shots (≈ 11u tall), Kuri u ≈ 26–36. Captions ≥ 64 px. Keep the
caption band (bottom ~200 px, or
pos: 'top') free of story-critical action. - Alive and acted. Moods change through
emotions()(anticipation squint → take → settle); actions have anticipation → action → follow-through (jump,spring,hair); something happens in every shot. - A transition at every seam: page turn (
{ turn }), crayon wipe, iris, fade from/to paper.
Engine gotchas
- Patched p5.brush.
studio.htmlloadsnode_modules/p5.brush/dist/p5.brush.crayon.js, written bypatch_brush.mjsonnpm install. Stock p5.brush darkens pigment where overlapping stamps saturate (right for ink, wrong for wax): butter-yellow turned mustard. If that file is missing, runnode patch_brush.mjs. - Cost = number of colour changes + stroke area. Each
colorInis ~3 brush passes; p5.brush composites a mask per colour change. Keep big background shapes sparse (hill/groundalready use wide gaps), don't scribble far off-screen. Typical: 150–250 ms/frame for a wide shot, ~350 ms for a busy medium shot with two characters and the page border, ~0.7–1 s during a page turn (two pages are drawn). - Page turn: the previous shot keeps being called with
lt > durforturnseconds — let its motion settle. - Don't name globals after p5's (
line,text,color,scale,shape…): the page never gets ready. - A
NaNin a point list throwsFailed to construct 'OffscreenCanvas'.**after a unary minus is a syntax error. - Strokes far from the origin under zoom ≳ 2 collapse (p5.brush);
paint/inkLine/colorIndraw around each shape's centre, which avoids it for normal sizes. - Google Fonts load over the network at page load; if a render times out on navigation, just re-run it.
src/karaoke.js(from painted-animation) still works for lyric videos; add its<script>if needed.
| 1 | |
| 2 | name crayon-storybook |
| 3 | description Make warm children's picture-book animations (MP4) drawn in crayon, coloured pencil and pastel on cream drawing paper — waxy crayon scribble fills broken by paper tooth, coloured-pencil outlines, slow 8 fps line boil, page-turn transitions, picture-book page framing and hand-lettered CJK storybook captions. p5.js + p5.brush rendered frame by frame in headless Chrome, encoded with ffmpeg. Use when a reference video or brief calls for a crayon / colored-pencil / pastel / kids' drawing / picture-book / bedtime-story / 繪本 / 蠟筆 / 色鉛筆 / 童書 look, or when the video-clone router picks the crayon-storybook style. |
| 4 | |
| 5 | |
| 6 | # Crayon storybook |
| 7 | |
| 8 | A drawing engine for ReelMimic. Every frame is a pure function of time `t`: `studio.html` loads p5.js + p5.brush, the |
| 9 | crayon look library, the cast and your scene files; each shot draws the whole 1920×1080 frame; `render.mjs` renders |
| 10 | contact sheets, strips and crops for review, or all frames in parallel, and ffmpeg encodes the MP4. |
| 11 | |
| 12 | **Provenance.** The runtime (`template/src/core.js`, `timeline.js`, `render.mjs`, `studio.html`, the render contract and |
| 13 | helpers: `paint`, `inkLine`, camera, `shots`, `glow`, `jump`/`take`/`spring`, `boilSeed`…) derives from the |
| 14 | **painted-animation** skill (ClaudeAnimationBase, MIT © John Heibel / tuzhechen2005 — see `template/LICENSE`). The |
| 15 | look (`crayon.js`), the characters (`cast.js`), the page turn, the page finish and the captions are this engine's own. |
| 16 | |
| 17 | ## Workflow |
| 18 | |
| 19 | **Scaffold** — `bash .claude/skills/crayon-storybook/scripts/new_project.sh <project-dir>` (add `--keep-demo` to keep |
| 20 | the example). It copies the template, runs `npm install` (which also writes the patched p5.brush build, see |
| 21 | gotchas) and checks node / ffmpeg / Chrome. Then **read [GUIDE.md] in full** and look at |
| 22 | `docs/demo-sheet.jpg` and `docs/cast.jpg` with Read. |
| 23 | **Storyboard** in writing: shots, reads (what the viewer must understand, when), transitions, captions. |
| 24 | **Build** — `src/config.js` (`duration`, `bpm`, `fps: 24`); one IIFE file per shot/scene in `src/scenes/`, ending with |
| 25 | `shots([...])`; add its `<script>` to `studio.html`. Captions: `captions([[t0, t1, '文字', { pos }]])` once. |
| 26 | **Look, fix, look again** (from the project dir): |
| 27 | |
| 28 | node render.mjs --sheet=0.5,1.5,2.5,3.5 --cols=4 --w=480 --out=out/check/a.jpg |
| 29 | node render.mjs --strip=2.0:2.6 --cols=6 --w=320 --out=out/check/strip.jpg |
| 30 | node render.mjs --sheet=2.3 --cols=1 --crop=600,200,900,700 --w=900 --out=out/check/face.jpg |
| 31 | |
| 32 | Open every image with Read. At least a sheet per shot, a strip per key action and per transition, a crop per face. |
| 33 | **Render** — `node render.mjs --frames --workers=4 && node render.mjs --encode --out=out/video.mp4 [--audio=…]`. |
| 34 | |
| 35 | ## API in one screen |
| 36 | |
| 37 | | area | calls | |
| 38 | |---|---| |
| 39 | | fills | `colorIn(pts, { col, fill: 'scribble'|'hatch'|'cross'|'smudge'|'flat', ang, gap, line, lw, shade })`, `scribbleFill`, `hatchFill`, `crossHatch`, `smudge`, `dab` | |
| 40 | | lines | `crayonLine`, `pencilLine`, `pastelLine`, `sketchLine`, `inkLine(pts, sw, col, brush)` — brushes `crayon`, `crayonFine`, `cpen`, `pastel`, `chalk`, `graphite` | |
| 41 | | shapes | `ellPts`, `rectPts`, `rrPts`, `starPts`, `heartPts`, `through`, `ribbon` (core) | |
| 42 | | scenery | `sky`, `sun`, `cloud`, `hill`/`hillY`, `ground`, `tufts`, `tree`, `flower`, `house`, `paperWash` | |
| 43 | | props | `apple`, `leafProp`, `starProp`, `heartProp`, `balloon`; emotes `emote(kind, x, y, s, k, age)` | |
| 44 | | cast | `lulu(x, y, u, o)`, `kuri(x, y, u, o)` → `{ head, top, handL, handR, feet }`; `feel`, `emotions`, `walkCycle`, `pose` | |
| 45 | | motion | `seg`, `kf`, `ease*`, `backOut`, `spring`, `ring`, `jump`, `take`, `stroll`, `arcPt`, `onTwos`, `pulse`, `shakeXY` | |
| 46 | | camera | `camBegin(cx, cy, zoom, rot)` … `camEnd()`, `toScreen` | |
| 47 | | staging | `shots([[t0, fn], [t1, fn, { turn: 1.0 }]])` (page turn), `bookPage(k)`, `vignette(k)`, `tooth(k)`, `crayonWipe(p)`, `iris`, `flash`, `glow` | |
| 48 | | text | `captions(list)` (storybook captions via `window.overlayHook`), `letter()` for words inside the picture | |
| 49 | |
| 50 | ## Rules that matter most |
| 51 | |
| 52 | **Crayon medium only.** Colour shapes in with `colorIn` (pale base + crayon scribble/hatch + coloured-pencil outline); |
| 53 | lines with the crayon/pencil brushes. No plain p5 `rect/ellipse/fill`, no gradients, no 3D. Turns go through the |
| 54 | drawn key views (`front`, `q`, `side`, `back`). |
| 55 | **Pure functions of `t`.** No `Math.random()`, no state between frames. `hash()` / `rnd(key)` for stable randomness; |
| 56 | `jit()` only for boil. Call `boilSeed(key)` before each separate background element/prop. |
| 57 | **Characters come from `cast.js` only.** Scenes pass poses (`aR`, `bR`, `view`, `walk`, `dy`, `sq`, `hair`, |
| 58 | `holdR`…) — they never draw a character's parts themselves. New characters go in `cast.js` (or one file each). |
| 59 | **Big and legible.** Lulu u ≈ 30–45 in medium shots (≈ 11u tall), Kuri u ≈ 26–36. Captions ≥ 64 px. Keep the |
| 60 | caption band (bottom ~200 px, or `pos: 'top'`) free of story-critical action. |
| 61 | **Alive and acted.** Moods change through `emotions()` (anticipation squint → take → settle); actions have |
| 62 | anticipation → action → follow-through (`jump`, `spring`, `hair`); something happens in every shot. |
| 63 | **A transition at every seam**: page turn (`{ turn }`), crayon wipe, iris, fade from/to paper. |
| 64 | |
| 65 | ## Engine gotchas |
| 66 | |
| 67 | **Patched p5.brush.** `studio.html` loads `node_modules/p5.brush/dist/p5.brush.crayon.js`, written by |
| 68 | `patch_brush.mjs` on `npm install`. Stock p5.brush darkens pigment where overlapping stamps saturate (right for ink, |
| 69 | wrong for wax): butter-yellow turned mustard. If that file is missing, run `node patch_brush.mjs`. |
| 70 | **Cost = number of colour changes + stroke area.** Each `colorIn` is ~3 brush passes; p5.brush composites a mask per |
| 71 | colour change. Keep big background shapes sparse (`hill`/`ground` already use wide gaps), don't scribble far |
| 72 | off-screen. Typical: 150–250 ms/frame for a wide shot, ~350 ms for a busy medium shot with two characters and the |
| 73 | page border, ~0.7–1 s during a page turn (two pages are drawn). |
| 74 | **Page turn:** the previous shot keeps being called with `lt > dur` for `turn` seconds — let its motion settle. |
| 75 | **Don't name globals after p5's** (`line`, `text`, `color`, `scale`, `shape`…): the page never gets ready. |
| 76 | A `NaN` in a point list throws `Failed to construct 'OffscreenCanvas'`. `**` after a unary minus is a syntax error. |
| 77 | Strokes far from the origin under zoom ≳ 2 collapse (p5.brush); `paint/inkLine/colorIn` draw around each shape's |
| 78 | centre, which avoids it for normal sizes. |
| 79 | Google Fonts load over the network at page load; if a render times out on navigation, just re-run it. |
| 80 | `src/karaoke.js` (from painted-animation) still works for lyric videos; add its `<script>` if needed. |
| 81 |
Discussion
Browse more free Claude skills.



