reel-studio 0.2.0 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -18,7 +18,8 @@ Fill sections 1–4 of the production README: the problem, the benefit, 3 hook v
18
18
  visual, **engine**, on-screen text, sound), caption. Pick engines with the `pick-engine` skill and say why.
19
19
  State size, length, placement and sound (VO / music / silent).
20
20
 
21
- **Stop and ask for approval.** No plates, audio or composition before the box is ticked.
21
+ **Stop and ask for approval** in chat. No plates, audio or composition before the box is ticked. (If the person uses
22
+ the optional desk, they may approve there instead: `review-desk` skill. Never tick the box on your own.)
22
23
 
23
24
  ## 2. Produce
24
25
 
@@ -0,0 +1,52 @@
1
+ ---
2
+ name: review-desk
3
+ description: Optional. Open the Reel Studio desk (a local review app) and work from its review notes - notes pinned to frames, renders, approvals. Use only when the person asks for the desk or already uses it - "open the desk", "apply my desk notes", "fix what I marked", "what's on this frame" - or a production has a review.json. Never required: reviewing and approving in chat works the same.
4
+ ---
5
+
6
+ # Review desk (optional)
7
+
8
+ The kit works the same without it: storyboards are approved in chat, and QA is the `make-video` steps. Don't start
9
+ the desk or suggest it unless the person asks, or they already use it (a `review.json` in the production).
10
+
11
+ The desk (`desk/`) is a local page for the person: it plays a production's composition frame-exact through the
12
+ renderer's own clock (`__seek`), and lets them pin notes to frames, render stills, preview videos and QA, read the
13
+ storyboard, approve, and send notes to Claude. It only reads and writes files in the project, so everything it does
14
+ reaches you as files (and, if it is registered, through the `reel-desk` MCP server).
15
+
16
+ ## Open it
17
+
18
+ `node desk/server.mjs` (run it in the background), then the person opens <http://127.0.0.1:4100>. In the Claude
19
+ desktop app, open that URL in the browser pane. `npx reel-studio desk` does the same. Port: `--port <n>`.
20
+
21
+ The `reel-desk` MCP tools below are not registered by default. If they aren't available and the person wants them:
22
+ `claude mcp add reel-desk --scope project -- node desk/mcp.mjs` (then restart the session). Without them, read and
23
+ write `review.json` directly.
24
+
25
+ ## Where things are
26
+
27
+ | | |
28
+ |---|---|
29
+ | Notes and approvals | `productions/<brand>/<ID>/review.json` (tracked in git: it is the review record) |
30
+ | What the person has open | `desk/.state.json`, or the `desk_state` tool |
31
+ | Desk renders | `productions/<brand>/<ID>/qa/desk/` (stills, `preview.mp4`, `qa/`); never the delivered export |
32
+
33
+ ## Working from notes
34
+
35
+ 1. `desk_state` tells you which production and time "this frame" means. `list_notes` gives the open notes
36
+ (`id`, `t` in seconds, `text`). Without the MCP server, read `review.json`.
37
+ 2. For each note: change the composition, check it with `render_frame` at the note's `t` (or
38
+ `node core/render.mjs still … --t <t>`) and **look at the image**.
39
+ 3. `resolve_note` with a one-line reply saying what changed. If a note needs a fact, an asset or a decision you
40
+ don't have, keep it `open` and put the question in the reply. Only the person closes notes (after checking).
41
+ 4. Your own QA findings can go on the timeline too: `add_note` (they show as by `claude`).
42
+
43
+ The desk's **Send to Claude** button runs exactly this loop headlessly (`claude -p` with the person's own Claude Code
44
+ login, `ANTHROPIC_API_KEY` removed, tools limited to read/edit/render/QA and `reel-desk`), one session per production
45
+ (`review.json` → `claude.session`).
46
+
47
+ ## Approvals are the person's
48
+
49
+ The desk's **Approve storyboard** ticks `- [x] Storyboard approved` in the production README and fills
50
+ *Approved by / date*; **Approve final cut** adds a final-cut line. Both are logged in `review.json` → `approvals`.
51
+ **Never write an approval yourself**, in the README or in `review.json`, and never treat a fixed note as approval.
52
+ When a storyboard is ready, say "it's in the desk for approval" and stop (golden rule 2).
@@ -15,6 +15,11 @@ description: Build a 3D shot for a video - live in the composition with Three.js
15
15
  to the import map). Wrap the load in `Studio.wait(...)`. Put model files in the brand's `assets/models/` or the production's `composition/`.
16
16
  - Cartoon look: `toon(color, { steps })` + `outline(mesh, { thickness })`. Hard light: one `DirectionalLight` + low `AmbientLight`.
17
17
  - Animated GLB clips: create an `AnimationMixer`, then in `update` call `mixer.setTime(t - start)`. Never `mixer.update(delta)`.
18
+ - HTML tags that follow 3D points: in `update(t)`, call `camera.updateMatrixWorld()` and the parent's
19
+ `updateMatrixWorld()` before `vector.applyMatrix4(obj.matrixWorld).project(camera)`. Three.js refreshes these only
20
+ inside `render()`, so without it a tag follows the previous frame. Example: `productions/bike-logger/REEL-002-engines`.
21
+ - CAD meshes as JSON (vertices + indices): `toNonIndexed()` + `computeVertexNormals()` gives crisp flat facets.
22
+ Render with `threeLayer(..., { pixelRatio: 2 })` for supersampled edges.
18
23
  - Physics: bake it (simulate at a fixed 1/120 s step into an array before the first frame) and look up by `t`.
19
24
 
20
25
  ## Blender (plate)
package/README.md CHANGED
@@ -1,13 +1,20 @@
1
1
  <h1 align="center">🎬 Reel Studio</h1>
2
2
 
3
3
  <p align="center">
4
+ <a href="https://github.com/murathany7/reel-studio/actions/workflows/ci.yml"><img src="https://github.com/murathany7/reel-studio/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
4
5
  <a href="https://www.npmjs.com/package/reel-studio"><img src="https://img.shields.io/npm/v/reel-studio?color=e8a33d" alt="npm"></a>
6
+ <a href="https://pypi.org/project/reel-studio/"><img src="https://img.shields.io/pypi/v/reel-studio?color=e8a33d" alt="PyPI"></a>
7
+ <a href="https://github.com/murathany7/homebrew-reel-studio"><img src="https://img.shields.io/badge/homebrew-tap-e8a33d" alt="Homebrew"></a>
5
8
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-3ecf7a" alt="MIT"></a>
6
9
  <img src="https://img.shields.io/badge/Claude%20Code-plugin-8b93a1" alt="Claude Code plugin">
7
10
  </p>
8
11
 
9
- <p align="center"><b>Make marketing reels, ads and posts with code, for any brand, with Claude Code.</b><br>
10
- Plain HTML pages on one clock → headless Chrome → MP4. Every frame is reproducible.</p>
12
+ <p align="center"><b>Agency-quality reels, ads and posts, made by Claude Code.</b><br>
13
+ Open source and free. Your Claude subscription does the rest.<br>
14
+ No editor, no timeline, no per-video fees. Every frame is code you own.</p>
15
+
16
+ <p align="center"><sub>Plain HTML pages on one clock → headless Chrome → MP4, for any brand. Every frame is reproducible.<br>
17
+ AI voiceover (ElevenLabs) is optional and billed by its provider.</sub></p>
11
18
 
12
19
  <p align="center">
13
20
  <img src="docs/media/bike-logger-reel.gif" width="240" alt="Bike Logger reel">
@@ -81,6 +88,20 @@ It copies the renderer into the folder, installs it and checks Chrome and ffmpeg
81
88
  npx reel-studio init
82
89
  ```
83
90
 
91
+ **Or with Python or Homebrew**, the same kit and the same `init` (the renderer still runs on Node):
92
+
93
+ ```bash
94
+ pipx install reel-studio && reel-studio init # or: uvx reel-studio init
95
+ brew install murathany7/reel-studio/reel-studio && reel-studio init # brings Node and ffmpeg
96
+ ```
97
+
98
+ **Or with Docker**, with Node, Chromium, ffmpeg and fonts inside, nothing to install (Linux renders use software WebGL):
99
+
100
+ ```bash
101
+ docker run --rm -it -v "$PWD":/work ghcr.io/murathany7/reel-studio reel-studio init
102
+ docker run --rm -v "$PWD":/work ghcr.io/murathany7/reel-studio node core/render.mjs video <page>.html out.mp4
103
+ ```
104
+
84
105
  **Or clone it** and work inside the repo:
85
106
 
86
107
  ```bash
@@ -93,6 +114,14 @@ cd core && npm install && cd ..
93
114
  > *Add my product as a new brand: https://my-product.com* → facts, colours, fonts, voice, rules
94
115
  > *Make a 15 s reel for &lt;brand&gt; about &lt;feature&gt;* → brief and storyboard, then the video once you approve
95
116
 
117
+ That's the whole product: you talk to Claude Code, it writes the storyboard, waits for your OK in chat, then makes,
118
+ renders and checks the video.
119
+
120
+ **Optional: the desk.** If you'd rather review on a page than in chat, run `npx reel-studio desk` (or
121
+ `node desk/server.mjs`) and open <http://127.0.0.1:4100>. You can scrub frame by frame, pin notes to frames, send them
122
+ to Claude Code (your own login, no API key), render stills and previews, and approve the storyboard and the final cut.
123
+ Nothing else needs it, and nothing changes if you never open it. See [`desk/README.md`](desk/README.md).
124
+
96
125
  ## What's inside
97
126
 
98
127
  ```
@@ -101,12 +130,17 @@ engines/ one adapter per engine
101
130
  brands/ one folder per brand (_template to start)
102
131
  productions/ one folder per piece (brief, composition, audio, QA, export)
103
132
  scripts/ audio mix, transcription, QA, frame tools
104
- .claude/skills setup · make-video · new-brand · pick-engine · scene-3d · character-2d · capture-site
133
+ desk/ optional local review app (and its optional reel-desk MCP server)
134
+ .claude/skills setup · make-video · new-brand · pick-engine · scene-3d · character-2d · capture-site · review-desk (optional)
105
135
  ```
106
136
 
107
137
  ## Contributing
108
138
 
109
- Open source and early. New engine adapters, brand examples and QA checks are all welcome; open an issue or a PR.
139
+ Open source and early. New engine adapters, skill fixes and QA checks are all welcome: see
140
+ [CONTRIBUTING.md](CONTRIBUTING.md). `npm test` re-renders the demo twice and checks every frame matches.
141
+
142
+ **Made something?** [Show your reel](https://github.com/murathany7/reel-studio/issues/new?template=show-your-reel.yml):
143
+ good examples get featured here. Questions go to [Discussions](https://github.com/murathany7/reel-studio/discussions).
110
144
 
111
145
  ## License
112
146
 
@@ -1,6 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  // npx reel-studio init [dir] [--update]: copy the Reel Studio kit and its Claude Code skills into a project.
3
+ // npx reel-studio desk [--port 4100]: open the review desk of the project in this folder.
3
4
  import { spawnSync } from "node:child_process";
5
+ import { existsSync } from "node:fs";
4
6
  import path from "node:path";
5
7
  import { fileURLToPath } from "node:url";
6
8
 
@@ -17,15 +19,27 @@ if (cmd === "init") {
17
19
  console.log(`
18
20
  Done. Open this folder in Claude Code and ask:
19
21
  "Add my product as a new brand: https://…"
20
- "Make a 15 s reel for <brand> about <feature>"`);
22
+ "Make a 15 s reel for <brand> about <feature>"
23
+ Optional, to review on a page instead of in chat: npx reel-studio desk`);
21
24
  }
22
25
  process.exit(r.status ?? 1);
23
26
  }
24
27
 
28
+ if (cmd === "desk") {
29
+ // The project's own copy, so the desk serves the project (its brands, productions and kit version)
30
+ const server = path.resolve("desk/server.mjs");
31
+ if (!existsSync(server)) {
32
+ console.error("No desk/ here. Run `npx reel-studio init` in this folder first (or `--update` an older project).");
33
+ process.exit(1);
34
+ }
35
+ process.exit(spawnSync(process.execPath, [server, ...rest], { stdio: "inherit" }).status ?? 1);
36
+ }
37
+
25
38
  console.log(`Reel Studio: marketing videos made with code, run by Claude Code.
26
39
 
27
40
  npx reel-studio init [dir] copy the kit and skills into dir (default: here) and install it
28
- npx reel-studio init [dir] --update refresh core/, engines/, scripts/ and skills from this version
41
+ npx reel-studio init [dir] --update refresh core/, engines/, scripts/, desk/ and skills from this version
42
+ npx reel-studio desk [--port 4100] optional review page: play, pin notes, send them to Claude, approve
29
43
 
30
44
  Claude Code plugin instead: /plugin marketplace add murathany7/reel-studio
31
45
  More: https://github.com/murathany7/reel-studio`);
@@ -86,7 +86,17 @@
86
86
  this.onSeek((t) => new Promise((res) => {
87
87
  const target = Math.max(0, Math.min(t - start, (el.duration || 0) - 0.001));
88
88
  if (Math.abs(el.currentTime - target) < 0.0005) return res();
89
- el.addEventListener("seeked", res, { once: true });
89
+ // "seeked" can fire before the new frame is painted, and a screenshot then catches the old one (seen on a
90
+ // cold CI runner): also wait for the frame to be presented. A seek within the same plate frame presents
91
+ // nothing new, so that wait gives up after a moment; the old frame is the right one then.
92
+ let seeked = false, shown = !el.requestVideoFrameCallback, timer;
93
+ const done = () => seeked && shown && (clearTimeout(timer), res());
94
+ if (!shown) el.requestVideoFrameCallback(() => { shown = true; done(); });
95
+ el.addEventListener("seeked", () => {
96
+ seeked = true;
97
+ timer = setTimeout(() => { shown = true; done(); }, 250);
98
+ done();
99
+ }, { once: true });
90
100
  el.currentTime = target;
91
101
  }));
92
102
  return el;
@@ -9,8 +9,8 @@
9
9
  "gsap": "^3.12.5",
10
10
  "lottie-web": "^5.12.2",
11
11
  "pixi.js": "^8.6.0",
12
- "playwright-core": "1.49.1",
13
- "three": "^0.170.0"
12
+ "playwright-core": "1.64.1",
13
+ "three": "^0.187.0"
14
14
  }
15
15
  },
16
16
  "node_modules/@pixi/colord": {
@@ -118,21 +118,21 @@
118
118
  }
119
119
  },
120
120
  "node_modules/playwright-core": {
121
- "version": "1.49.1",
122
- "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.49.1.tgz",
123
- "integrity": "sha512-BzmpVcs4kE2CH15rWfzpjzVGhWERJfmnXmniSyKeRZUs9Ws65m+RGIi7mjJK/euCegfn3i7jvqWeWyHe9y3Vgg==",
121
+ "version": "1.64.1",
122
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.64.1.tgz",
123
+ "integrity": "sha512-BC4s8y6SEVA6Q1rSxZTto8hI1KUI3xXlHGZgvGqP4IqPdI/a6FX9H29IDNz7vKo9l21b6LfYosScPL6M3xxyDQ==",
124
124
  "license": "Apache-2.0",
125
125
  "bin": {
126
126
  "playwright-core": "cli.js"
127
127
  },
128
128
  "engines": {
129
- "node": ">=18"
129
+ "node": ">=20"
130
130
  }
131
131
  },
132
132
  "node_modules/three": {
133
- "version": "0.170.0",
134
- "resolved": "https://registry.npmjs.org/three/-/three-0.170.0.tgz",
135
- "integrity": "sha512-FQK+LEpYc0fBD+J8g6oSEyyNzjp+Q7Ks1C568WWaoMRLW+TkNNWmenWeGgJjV105Gd+p/2ql1ZcjYvNiPZBhuQ==",
133
+ "version": "0.187.0",
134
+ "resolved": "https://registry.npmjs.org/three/-/three-0.187.0.tgz",
135
+ "integrity": "sha512-Qiu7R+Cmh8CstZsY1uJOSWnVlfo7ZtNLV7mF8Jltk3LB3AtDu9cRZlcnRKkAk8Y4z6KKw35z7Y7Urd7jWY8etA==",
136
136
  "license": "MIT"
137
137
  },
138
138
  "node_modules/tiny-lru": {
package/core/package.json CHANGED
@@ -10,7 +10,7 @@
10
10
  "gsap": "^3.12.5",
11
11
  "lottie-web": "^5.12.2",
12
12
  "pixi.js": "^8.6.0",
13
- "playwright-core": "1.49.1",
14
- "three": "^0.170.0"
13
+ "playwright-core": "1.64.1",
14
+ "three": "^0.187.0"
15
15
  }
16
16
  }
package/core/render.mjs CHANGED
@@ -78,8 +78,10 @@ function serve() {
78
78
  // WebGL (Three.js, Pixi) on the GPU, but compositing in software: with GPU compositing Chrome
79
79
  // draws <video> plates ~10 levels brighter than ffmpeg decodes them (measured 2026-11-02).
80
80
  // playwright-core is pinned (1.49.1) because 1.64 shifted plate colours the same way.
81
- const CHROME_ARGS = ["--force-color-profile=srgb", "--hide-scrollbars",
82
- "--enable-gpu", "--use-angle=metal", "--ignore-gpu-blocklist", "--disable-gpu-compositing"];
81
+ // WebGL: Metal on macOS; elsewhere (Linux, CI without a GPU) Chrome's software renderer, SwiftShader
82
+ const CHROME_ARGS = ["--force-color-profile=srgb", "--hide-scrollbars", "--enable-gpu", "--ignore-gpu-blocklist",
83
+ "--disable-gpu-compositing",
84
+ ...(process.platform === "darwin" ? ["--use-angle=metal"] : ["--use-angle=swiftshader", "--enable-unsafe-swiftshader"])];
83
85
 
84
86
  async function openPage(input, safe) {
85
87
  const server = await serve();
package/desk/README.md ADDED
@@ -0,0 +1,47 @@
1
+ # Desk (optional)
2
+
3
+ A local review app for productions, **for people who'd rather review on a page than in chat**. Nothing in the kit
4
+ needs it: the skills are the product, and Claude Code writes, renders and checks the pieces either way. If you never
5
+ open the desk, nothing changes. It adds no dependencies and doesn't register itself anywhere.
6
+
7
+ ```bash
8
+ node desk/server.mjs # or: npx reel-studio desk → http://127.0.0.1:4100
9
+ ```
10
+
11
+ | Panel | What it does |
12
+ |---|---|
13
+ | Player | Loads `composition/index.html?render=1` and drives it with `__ready`, `__meta`, `__seek`, the same calls as `core/render.mjs`. Space play/pause, ←/→ one frame (⇧ one second), S safe zones, N new note |
14
+ | Notes | Pin a note to the current frame. Dots on the scrubber. Claude marks notes *fixed* with a reply; you close them after checking |
15
+ | Claude | Sends the open notes and/or a request to a headless Claude Code run and streams what it does. The composition reloads when it's done |
16
+ | Render | Still at this frame, preview video, QA (`scripts/qa-video.sh`). Output goes to `qa/desk/`, never `export/` |
17
+ | Approve | Storyboard (ticks the README box) and final cut. People only: Claude and the MCP server can't approve |
18
+ | Storyboard | The production README |
19
+
20
+ ## How Claude is involved, and what it costs
21
+
22
+ There are three ways, and none needs an API key.
23
+
24
+ 1. **Files.** Notes and approvals live in `productions/<brand>/<ID>/review.json`. In any Claude Code session say
25
+ "apply my desk notes" (the `review-desk` skill).
26
+ 2. **Send to Claude** runs `claude -p` (the Claude Code CLI on this machine, signed in as you) from the project root.
27
+ `ANTHROPIC_API_KEY` is removed from its environment, so it always uses your Claude Code login. Its tools are limited
28
+ to Read/Edit/Write/Glob/Grep, `node core/render.mjs`, `scripts/qa-video.sh`, `scripts/extract-frames.sh` and the
29
+ `reel-desk` tools. It runs one session per production (`--resume`). Set `CLAUDE_BIN` to use another `claude`.
30
+ 3. **`reel-desk` MCP server** (`desk/mcp.mjs`), optional on top of the optional desk, and not registered by default.
31
+ Add it with `claude mcp add reel-desk --scope project -- node desk/mcp.mjs`. It lets a Claude Code session see the desk:
32
+ `desk_state`, `list_productions`, `list_notes`, `add_note`, `resolve_note`, `render_frame` (returns the image).
33
+ It has no approval tool.
34
+
35
+ ## Safety
36
+
37
+ The server listens on 127.0.0.1 only, serves files from the project root only (not `.git` or `.env*`), and refuses
38
+ API calls from other origins, so another site open in your browser can't drive it. It has no dependencies.
39
+
40
+ ## Files
41
+
42
+ | | |
43
+ |---|---|
44
+ | `server.mjs` | HTTP server: static files with range requests (for plates) + the JSON API + job streams (SSE) |
45
+ | `lib.mjs` | Productions, notes, approvals, render commands, the Claude prompt. Shared by the server and the MCP server |
46
+ | `mcp.mjs` | The `reel-desk` MCP server (stdio JSON-RPC) |
47
+ | `index.html`, `app.js`, `desk.css` | The page. No build step |