Crayon storybook skill

Crayon storybook — Sugar Rush (from the edenfunf/reelmimic README)

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 ↗

Use now
Crayon storybook — Sugar Rush (from the edenfunf/reelmimic README)

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

edenfunf/main1 file shown
SKILL.md
Show the full text81 lines
crayon-storybook/SKILL.md81 lines · 6.7 KB

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

  1. Scaffold — bash .claude/skills/crayon-storybook/scripts/new_project.sh <project-dir> (add --keep-demo to keep the example). It copies the template, runs npm install (which also writes the patched p5.brush build, see gotchas) and checks node / ffmpeg / Chrome. Then read GUIDE.md in full and look at docs/demo-sheet.jpg and docs/cast.jpg with Read.
  2. Storyboard in writing: shots, reads (what the viewer must understand, when), transitions, captions.
  3. Build — src/config.js (duration, bpm, fps: 24); one IIFE file per shot/scene in src/scenes/, ending with shots([...]); add its <script> to studio.html. Captions: captions([[t0, t1, '文字', { pos }]]) once.
  4. Look, fix, look again (from the project dir):
    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
    
    Open every image with Read. At least a sheet per shot, a strip per key action and per transition, a crop per face.
  5. 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

  1. Crayon medium only. Colour shapes in with colorIn (pale base + crayon scribble/hatch + coloured-pencil outline); lines with the crayon/pencil brushes. No plain p5 rect/ellipse/fill, no gradients, no 3D. Turns go through the drawn key views (front, q, side, back).
  2. Pure functions of t. No Math.random(), no state between frames. hash() / rnd(key) for stable randomness; jit() only for boil. Call boilSeed(key) before each separate background element/prop.
  3. Characters come from cast.js only. Scenes pass poses (aR, bR, view, walk, dy, sq, hair, holdR…) — they never draw a character's parts themselves. New characters go in cast.js (or one file each).
  4. 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.
  5. 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.
  6. A transition at every seam: page turn ({ turn }), crayon wipe, iris, fade from/to paper.

Engine gotchas

  • Patched p5.brush. studio.html loads node_modules/p5.brush/dist/p5.brush.crayon.js, written by patch_brush.mjs on npm 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, run node patch_brush.mjs.
  • Cost = number of colour changes + stroke area. Each colorIn is ~3 brush passes; p5.brush composites a mask per colour change. Keep big background shapes sparse (hill/ground already 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 > dur for turn seconds — let its motion settle.
  • Don't name globals after p5's (line, text, color, scale, shape…): the page never gets ready.
  • A NaN in a point list throws Failed to construct 'OffscreenCanvas'. ** after a unary minus is a syntax error.
  • Strokes far from the origin under zoom ≳ 2 collapse (p5.brush); paint/inkLine/colorIn draw 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---
2name: crayon-storybook
3description: 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 
8A drawing engine for ReelMimic. Every frame is a pure function of time `t`: `studio.html` loads p5.js + p5.brush, the
9crayon look library, the cast and your scene files; each shot draws the whole 1920×1080 frame; `render.mjs` renders
10contact 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
13helpers: `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
15look (`crayon.js`), the characters (`cast.js`), the page turn, the page finish and the captions are this engine's own.
16 
17## Workflow
18 
191. **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](GUIDE.md) in full** and look at
22 `docs/demo-sheet.jpg` and `docs/cast.jpg` with Read.
232. **Storyboard** in writing: shots, reads (what the viewer must understand, when), transitions, captions.
243. **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.
264. **Look, fix, look again** (from the project dir):
27 ```bash
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.
335. **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 
521. **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`).
552. **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.
573. **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).
594. **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.
615. **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.
636. **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