explainer-studio 0.1.5__tar.gz

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.
Files changed (37) hide show
  1. explainer_studio-0.1.5/.gitignore +231 -0
  2. explainer_studio-0.1.5/LICENSE +41 -0
  3. explainer_studio-0.1.5/PKG-INFO +149 -0
  4. explainer_studio-0.1.5/README.md +133 -0
  5. explainer_studio-0.1.5/brand/brand.css +145 -0
  6. explainer_studio-0.1.5/brand/fonts/JetBrainsMono.ttf +0 -0
  7. explainer_studio-0.1.5/brand/fonts/OFL-JetBrainsMono.txt +93 -0
  8. explainer_studio-0.1.5/brand/fonts/OFL-PTSerif.txt +93 -0
  9. explainer_studio-0.1.5/brand/fonts/OFL-SourceSans3.txt +93 -0
  10. explainer_studio-0.1.5/brand/fonts/PTSerif-Bold.ttf +0 -0
  11. explainer_studio-0.1.5/brand/fonts/PTSerif-BoldItalic.ttf +0 -0
  12. explainer_studio-0.1.5/brand/fonts/PTSerif-Italic.ttf +0 -0
  13. explainer_studio-0.1.5/brand/fonts/PTSerif-Regular.ttf +0 -0
  14. explainer_studio-0.1.5/brand/fonts/SourceSans3-Bold.ttf +0 -0
  15. explainer_studio-0.1.5/brand/fonts/SourceSans3-Italic.ttf +0 -0
  16. explainer_studio-0.1.5/brand/fonts/SourceSans3.ttf +0 -0
  17. explainer_studio-0.1.5/brand/frame.md +108 -0
  18. explainer_studio-0.1.5/brand/pronunciations.yaml +8 -0
  19. explainer_studio-0.1.5/brand/vendor/gsap.min.js +11 -0
  20. explainer_studio-0.1.5/pyproject.toml +56 -0
  21. explainer_studio-0.1.5/templates/scenes/blank.html +46 -0
  22. explainer_studio-0.1.5/templates/scenes/outro.html +50 -0
  23. explainer_studio-0.1.5/templates/scenes/points.html +55 -0
  24. explainer_studio-0.1.5/templates/scenes/quote.html +41 -0
  25. explainer_studio-0.1.5/templates/scenes/stat.html +57 -0
  26. explainer_studio-0.1.5/templates/scenes/title.html +46 -0
  27. explainer_studio-0.1.5/templates/script.md +51 -0
  28. explainer_studio-0.1.5/templates/thumbnail.html +28 -0
  29. explainer_studio-0.1.5/tools/studio/__init__.py +0 -0
  30. explainer_studio-0.1.5/tools/studio/build.py +196 -0
  31. explainer_studio-0.1.5/tools/studio/chatterbox_worker.py +96 -0
  32. explainer_studio-0.1.5/tools/studio/cli.py +265 -0
  33. explainer_studio-0.1.5/tools/studio/common.py +160 -0
  34. explainer_studio-0.1.5/tools/studio/music.py +66 -0
  35. explainer_studio-0.1.5/tools/studio/render.py +271 -0
  36. explainer_studio-0.1.5/tools/studio/script.py +158 -0
  37. explainer_studio-0.1.5/tools/studio/voice.py +633 -0
@@ -0,0 +1,231 @@
1
+ .venv/
2
+ __pycache__/
3
+ episodes/*/build/
4
+ episodes/*/audio/tts/
5
+ episodes/*/assets/brand/
6
+ episodes/*/.hyperframes/
7
+ episodes/*/_render_*.html
8
+ out/
9
+ .env
10
+
11
+ # --- GitHub Python template ---
12
+ # Byte-compiled / optimized / DLL files
13
+ __pycache__/
14
+ *.py[codz]
15
+ *$py.class
16
+
17
+ # C extensions
18
+ *.so
19
+
20
+ # Distribution / packaging
21
+ .Python
22
+ build/
23
+ develop-eggs/
24
+ dist/
25
+ downloads/
26
+ eggs/
27
+ .eggs/
28
+ lib/
29
+ lib64/
30
+ parts/
31
+ sdist/
32
+ var/
33
+ wheels/
34
+ share/python-wheels/
35
+ *.egg-info/
36
+ .installed.cfg
37
+ *.egg
38
+ MANIFEST
39
+
40
+ # PyInstaller
41
+ # Usually these files are written by a python script from a template
42
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
43
+ *.manifest
44
+ *.spec
45
+
46
+ # Installer logs
47
+ pip-log.txt
48
+ pip-delete-this-directory.txt
49
+
50
+ # Unit test / coverage reports
51
+ htmlcov/
52
+ .tox/
53
+ .nox/
54
+ .coverage
55
+ .coverage.*
56
+ .cache
57
+ nosetests.xml
58
+ coverage.xml
59
+ *.cover
60
+ *.py.cover
61
+ *.lcov
62
+ .hypothesis/
63
+ .pytest_cache/
64
+ cover/
65
+
66
+ # Translations
67
+ *.mo
68
+ *.pot
69
+
70
+ # Django stuff:
71
+ *.log
72
+ local_settings.py
73
+ db.sqlite3
74
+ db.sqlite3-journal
75
+
76
+ # Flask stuff:
77
+ instance/
78
+ .webassets-cache
79
+
80
+ # Scrapy stuff:
81
+ .scrapy
82
+
83
+ # Sphinx documentation
84
+ docs/_build/
85
+
86
+ # PyBuilder
87
+ .pybuilder/
88
+ target/
89
+
90
+ # Jupyter Notebook
91
+ .ipynb_checkpoints
92
+
93
+ # IPython
94
+ profile_default/
95
+ ipython_config.py
96
+
97
+ # pyenv
98
+ # For a library or package, you might want to ignore these files since the code is
99
+ # intended to run in multiple environments; otherwise, check them in:
100
+ # .python-version
101
+
102
+ # pipenv
103
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
104
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
105
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
106
+ # install all needed dependencies.
107
+ # Pipfile.lock
108
+
109
+ # uv
110
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
111
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
112
+ # commonly ignored for libraries.
113
+ # uv.lock
114
+
115
+ # poetry
116
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
117
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
118
+ # commonly ignored for libraries.
119
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
120
+ # poetry.lock
121
+ # poetry.toml
122
+
123
+ # pdm
124
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
125
+ # pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
126
+ # https://pdm-project.org/en/latest/usage/project/#working-with-version-control
127
+ # pdm.lock
128
+ # pdm.toml
129
+ .pdm-python
130
+ .pdm-build/
131
+
132
+ # pixi
133
+ # Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
134
+ # pixi.lock
135
+ # Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
136
+ # in the .venv directory. It is recommended not to include this directory in version control.
137
+ .pixi/*
138
+ !.pixi/config.toml
139
+
140
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
141
+ __pypackages__/
142
+
143
+ # Celery stuff
144
+ celerybeat-schedule*
145
+ celerybeat.pid
146
+
147
+ # Redis
148
+ *.rdb
149
+ *.aof
150
+ *.pid
151
+
152
+ # RabbitMQ
153
+ mnesia/
154
+ rabbitmq/
155
+ rabbitmq-data/
156
+
157
+ # ActiveMQ
158
+ activemq-data/
159
+
160
+ # SageMath parsed files
161
+ *.sage.py
162
+
163
+ # Environments
164
+ .env
165
+ .envrc
166
+ .venv
167
+ env/
168
+ venv/
169
+ ENV/
170
+ env.bak/
171
+ venv.bak/
172
+
173
+ # Spyder project settings
174
+ .spyderproject
175
+ .spyproject
176
+
177
+ # Rope project settings
178
+ .ropeproject
179
+
180
+ # mkdocs/Zensical documentation
181
+ /site
182
+
183
+ # mypy
184
+ .mypy_cache/
185
+ .dmypy.json
186
+ dmypy.json
187
+
188
+ # Pyre type checker
189
+ .pyre/
190
+
191
+ # pytype static type analyzer
192
+ .pytype/
193
+
194
+ # Cython debug symbols
195
+ cython_debug/
196
+
197
+ # PyCharm
198
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
199
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
200
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
201
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
202
+ # .idea/
203
+
204
+ # Abstra
205
+ # Abstra is an AI-powered process automation framework.
206
+ # Ignore directories containing user credentials, local state, and settings.
207
+ # Learn more at https://abstra.io/docs
208
+ .abstra/
209
+
210
+ # Visual Studio Code
211
+ # Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore that
212
+ # can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
213
+ # and can be added to the global gitignore or merged into this file. However, if you prefer, you
214
+ # could uncomment the following to ignore the entire vscode folder
215
+ # .vscode/
216
+ # Temporary file for partial code execution
217
+ tempCodeRunnerFile.py
218
+
219
+ # Ruff stuff:
220
+ .ruff_cache/
221
+
222
+ # PyPI configuration file
223
+ .pypirc
224
+
225
+ # Marimo
226
+ marimo/_static/
227
+ marimo/_lsp/
228
+ __marimo__/
229
+
230
+ # Streamlit
231
+ .streamlit/secrets.toml
@@ -0,0 +1,41 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 World Socialist Web Site
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
23
+ -------------------------------------------------------------------------------
24
+ Scope
25
+
26
+ The MIT licence above covers the studio software and its documentation:
27
+ tools/, templates/, brand/brand.css, brand/frame.md, brand/pronunciations.yaml,
28
+ docs/, the .github/ workflows, pyproject.toml, README.md, AGENTS.md and CLAUDE.md.
29
+
30
+ It does NOT cover editorial content. Everything under episodes/ and scripts/
31
+ (scripts, narration audio, timings, scene designs, thumbnails and the videos
32
+ made from them) is Copyright (c) 2026 World Socialist Web Site, all rights
33
+ reserved.
34
+
35
+ Third-party files keep their own licences:
36
+ - brand/fonts/: PT Serif, Source Sans 3 and JetBrains Mono, SIL Open Font
37
+ License 1.1 (see the OFL-*.txt files alongside them).
38
+ - brand/vendor/gsap.min.js: GSAP, GreenSock Standard License
39
+ (https://gsap.com/standard-license), as stated in the file header.
40
+ - episodes/*/assets/media/: photographs, maps and footage remain under the
41
+ terms of their original sources, credited on screen where they are used.
@@ -0,0 +1,149 @@
1
+ Metadata-Version: 2.5
2
+ Name: explainer-studio
3
+ Version: 0.1.5
4
+ Summary: Command-line studio for narrated explainer videos (HyperFrames + Kokoro TTS + ffmpeg)
5
+ License-Expression: MIT
6
+ License-File: LICENSE
7
+ Requires-Python: <3.14,>=3.12
8
+ Requires-Dist: kokoro-onnx>=0.6.1
9
+ Requires-Dist: misaki>=0.9
10
+ Requires-Dist: num2words>=0.5
11
+ Requires-Dist: numpy>=2.0
12
+ Requires-Dist: pyyaml>=6.0
13
+ Requires-Dist: soundfile>=0.12
14
+ Requires-Dist: spacy<3.9,>=3.8
15
+ Description-Content-Type: text/markdown
16
+
17
+ # Explainer Studio
18
+
19
+ [![CI](https://github.com/JoeMountWSWS/explainer-studio/actions/workflows/ci.yml/badge.svg)](https://github.com/JoeMountWSWS/explainer-studio/actions/workflows/ci.yml)
20
+
21
+ A command-line studio for narrated explainer videos in the style of [wsws.org](https://www.wsws.org).
22
+ You write a script in Markdown. The studio narrates it with local TTS, aligns every word to the audio,
23
+ and turns each scene into a [HyperFrames](https://www.npmjs.com/package/hyperframes) HTML composition
24
+ that is timed to the narration. It then renders a mastered 1920×1080 MP4 for YouTube, with captions
25
+ and chapter markers.
26
+
27
+ ```
28
+ script.md ──► narration (Kokoro, local; or ElevenLabs) ──► word timings (Whisper)
29
+ ──► scenes/<id>.html (HyperFrames + GSAP, timed to cues) ──► cached per-scene renders
30
+ ──► out/<slug>.mp4 (−14 LUFS) + out/<slug>.srt + out/<slug>.chapters.txt
31
+ ```
32
+
33
+ # Installation
34
+
35
+ ## Requirements
36
+
37
+ - Linux (developed on Arch with an AMD GPU; the render uses Chromium's GPU capture)
38
+ - [uv](https://docs.astral.sh/uv/), with Python 3.12 or 3.13 (`kokoro-onnx` doesn't support 3.14 yet)
39
+ - Node.js with `npx`. The HyperFrames CLI is fetched on demand at the version pinned in `tools/studio/common.py`.
40
+ - ffmpeg and Chromium
41
+ - Optional: an ElevenLabs API key in `.env` (`ELEVENLABS_API_KEY=…`) for cloud narration
42
+
43
+ The first `sync` downloads the Kokoro-82M model to `~/.cache/hyperframes/tts` and spaCy's
44
+ `en_core_web_sm` to `~/.cache/hyperframes/spacy`.
45
+
46
+ # Usage
47
+
48
+ Making a video with the studio is a loop of writing and listening. You write the script as plain
49
+ Markdown, and the studio reads it aloud, works out exactly when each word is spoken, and hands those
50
+ timings to your scenes so the visuals move in step with the voice. Change a sentence and only that
51
+ sentence is re-recorded and only the affected scenes are re-rendered, so you can refine a five-minute
52
+ video in seconds rather than starting over. Everything runs from one command, `uv run studio`, and
53
+ nothing leaves your machine unless you choose cloud narration.
54
+
55
+ ## Install
56
+
57
+ With the [requirements](#requirements) in place, either install the `studio` command from PyPI or
58
+ clone the repository.
59
+
60
+ **From PyPI**, to make videos in a folder of your own:
61
+
62
+ ```bash
63
+ uv tool install explainer-studio # or run it without installing: uvx --from explainer-studio studio …
64
+ mkdir my-videos && cd my-videos
65
+ studio new my-video "My Video"
66
+ ```
67
+
68
+ The folder you run `studio` in becomes the workspace. It holds `episodes/`, `out/` and `.env`, and
69
+ `studio` finds it from any subdirectory (or set `STUDIO_ROOT`). The house style (`brand/`) and scene
70
+ templates ship with the package. To customise them, copy `brand/` or `templates/` from this repository
71
+ into the workspace, and the studio uses your copy instead. Music beds and voice clips go in the
72
+ workspace's `brand/music/` and `brand/voices/`. Use `studio` wherever this README says `uv run studio`.
73
+
74
+ **From a clone**, to work on the studio itself or to use the example episodes:
75
+
76
+ ```bash
77
+ git clone git@github.com:JoeMountWSWS/explainer-studio.git
78
+ cd explainer-studio
79
+ uv sync
80
+ ```
81
+
82
+ Run commands from the repository root (or any folder below it). Confirm the install with
83
+ `uv run studio --help`. The first `sync` of an episode downloads the Kokoro voice model and the
84
+ spaCy English model, so it takes a little longer than later runs.
85
+
86
+ ## Quick start
87
+
88
+ ```bash
89
+ uv run studio new my-video "My Video" # scaffold episodes/my-video/
90
+ $EDITOR episodes/my-video/script.md # write the script
91
+ uv run studio sync my-video # narrate, align, stub scene files, write index.html
92
+ uv run studio status my-video # words, estimated vs actual length, cues per scene
93
+ # design each episodes/my-video/scenes/<id>.html
94
+ uv run studio preview my-video # HyperFrames Studio in the browser, with narration
95
+ uv run studio check my-video # lint, layout and contrast audits (must pass)
96
+ uv run studio render my-video --draft # quick review copy -> out/my-video-draft.mp4
97
+ uv run studio render my-video # final -> out/my-video.mp4 + .srt + .chapters.txt
98
+ uv run studio thumbnail my-video # out/my-video-thumbnail.jpg
99
+ uv run studio voices my-video # audition Kokoro voice blends -> out/voice-samples/
100
+ ```
101
+
102
+ Only scenes that have changed are re-rendered. Changing a paragraph of narration re-synthesises
103
+ that paragraph and nothing else, and re-times the visuals that depend on it.
104
+
105
+ ## Writing a script
106
+
107
+ ```markdown
108
+ ---
109
+ title: The Paris Commune
110
+ voice: bf_emma*0.6 + bf_isabella*0.4
111
+ speed: 0.85
112
+ ---
113
+ ## hook: Seventy-two days
114
+ > template: title
115
+
116
+ In the spring of 1871, the working class took power in a major city. {sub} This is the story of the Paris Commune.
117
+ ```
118
+
119
+ Each `## id: Chapter title` heading is a scene and a YouTube chapter. `> template:` chooses the
120
+ starting pattern for the scene file (`title`, `stat`, `quote`, `points`, `outro`, `blank`). Other `>`
121
+ lines are visual notes and are never spoken. `{name}` marks a cue that the scene can animate on,
122
+ and `{pause 1.5}` inserts a pause.
123
+
124
+ The full documentation is at <https://joemountwsws.github.io/explainer-studio/>. [AGENTS.md](AGENTS.md) is the reference for coding agents: every front-matter setting, voice choice, the rules for
125
+ authoring scenes, the render pipeline and known problems. Read [brand/frame.md](brand/frame.md)
126
+ before designing scenes.
127
+
128
+ ## Layout
129
+
130
+ | Path | What it is |
131
+ | --- | --- |
132
+ | `tools/studio/` | The Python CLI (`uv run studio …`) |
133
+ | `brand/` | House style shared by every episode: design spec, CSS, fonts, GSAP, pronunciations |
134
+ | `templates/` | Starter script, scene patterns and thumbnail |
135
+ | `episodes/<slug>/` | One HyperFrames project per video. `script.md` is the source of truth |
136
+ | `docs/` | The documentation site (MkDocs), including notes such as British ElevenLabs voices |
137
+ | `out/` | Rendered deliverables (gitignored) |
138
+
139
+ Worked examples: `episodes/demo-paris-commune` (Kokoro, 38s) and `episodes/demo-elevenlabs`
140
+ (the same script narrated by ElevenLabs).
141
+
142
+ ## Working with coding agents
143
+
144
+ The studio is designed to be driven by Claude Code or another coding agent. `AGENTS.md` (loaded
145
+ through `CLAUDE.md`) gives the agent the conventions it needs, the HyperFrames skills cover the
146
+ composition format and motion, and the `make-explainer-video` skill
147
+ (`.claude/skills/make-explainer-video/`) walks Claude through the whole workflow. Start `claude` in
148
+ the repository root and ask for a video, or run `/make-explainer-video`. See
149
+ [Working with a coding agent](https://joemountwsws.github.io/explainer-studio/coding-agents/).
@@ -0,0 +1,133 @@
1
+ # Explainer Studio
2
+
3
+ [![CI](https://github.com/JoeMountWSWS/explainer-studio/actions/workflows/ci.yml/badge.svg)](https://github.com/JoeMountWSWS/explainer-studio/actions/workflows/ci.yml)
4
+
5
+ A command-line studio for narrated explainer videos in the style of [wsws.org](https://www.wsws.org).
6
+ You write a script in Markdown. The studio narrates it with local TTS, aligns every word to the audio,
7
+ and turns each scene into a [HyperFrames](https://www.npmjs.com/package/hyperframes) HTML composition
8
+ that is timed to the narration. It then renders a mastered 1920×1080 MP4 for YouTube, with captions
9
+ and chapter markers.
10
+
11
+ ```
12
+ script.md ──► narration (Kokoro, local; or ElevenLabs) ──► word timings (Whisper)
13
+ ──► scenes/<id>.html (HyperFrames + GSAP, timed to cues) ──► cached per-scene renders
14
+ ──► out/<slug>.mp4 (−14 LUFS) + out/<slug>.srt + out/<slug>.chapters.txt
15
+ ```
16
+
17
+ # Installation
18
+
19
+ ## Requirements
20
+
21
+ - Linux (developed on Arch with an AMD GPU; the render uses Chromium's GPU capture)
22
+ - [uv](https://docs.astral.sh/uv/), with Python 3.12 or 3.13 (`kokoro-onnx` doesn't support 3.14 yet)
23
+ - Node.js with `npx`. The HyperFrames CLI is fetched on demand at the version pinned in `tools/studio/common.py`.
24
+ - ffmpeg and Chromium
25
+ - Optional: an ElevenLabs API key in `.env` (`ELEVENLABS_API_KEY=…`) for cloud narration
26
+
27
+ The first `sync` downloads the Kokoro-82M model to `~/.cache/hyperframes/tts` and spaCy's
28
+ `en_core_web_sm` to `~/.cache/hyperframes/spacy`.
29
+
30
+ # Usage
31
+
32
+ Making a video with the studio is a loop of writing and listening. You write the script as plain
33
+ Markdown, and the studio reads it aloud, works out exactly when each word is spoken, and hands those
34
+ timings to your scenes so the visuals move in step with the voice. Change a sentence and only that
35
+ sentence is re-recorded and only the affected scenes are re-rendered, so you can refine a five-minute
36
+ video in seconds rather than starting over. Everything runs from one command, `uv run studio`, and
37
+ nothing leaves your machine unless you choose cloud narration.
38
+
39
+ ## Install
40
+
41
+ With the [requirements](#requirements) in place, either install the `studio` command from PyPI or
42
+ clone the repository.
43
+
44
+ **From PyPI**, to make videos in a folder of your own:
45
+
46
+ ```bash
47
+ uv tool install explainer-studio # or run it without installing: uvx --from explainer-studio studio …
48
+ mkdir my-videos && cd my-videos
49
+ studio new my-video "My Video"
50
+ ```
51
+
52
+ The folder you run `studio` in becomes the workspace. It holds `episodes/`, `out/` and `.env`, and
53
+ `studio` finds it from any subdirectory (or set `STUDIO_ROOT`). The house style (`brand/`) and scene
54
+ templates ship with the package. To customise them, copy `brand/` or `templates/` from this repository
55
+ into the workspace, and the studio uses your copy instead. Music beds and voice clips go in the
56
+ workspace's `brand/music/` and `brand/voices/`. Use `studio` wherever this README says `uv run studio`.
57
+
58
+ **From a clone**, to work on the studio itself or to use the example episodes:
59
+
60
+ ```bash
61
+ git clone git@github.com:JoeMountWSWS/explainer-studio.git
62
+ cd explainer-studio
63
+ uv sync
64
+ ```
65
+
66
+ Run commands from the repository root (or any folder below it). Confirm the install with
67
+ `uv run studio --help`. The first `sync` of an episode downloads the Kokoro voice model and the
68
+ spaCy English model, so it takes a little longer than later runs.
69
+
70
+ ## Quick start
71
+
72
+ ```bash
73
+ uv run studio new my-video "My Video" # scaffold episodes/my-video/
74
+ $EDITOR episodes/my-video/script.md # write the script
75
+ uv run studio sync my-video # narrate, align, stub scene files, write index.html
76
+ uv run studio status my-video # words, estimated vs actual length, cues per scene
77
+ # design each episodes/my-video/scenes/<id>.html
78
+ uv run studio preview my-video # HyperFrames Studio in the browser, with narration
79
+ uv run studio check my-video # lint, layout and contrast audits (must pass)
80
+ uv run studio render my-video --draft # quick review copy -> out/my-video-draft.mp4
81
+ uv run studio render my-video # final -> out/my-video.mp4 + .srt + .chapters.txt
82
+ uv run studio thumbnail my-video # out/my-video-thumbnail.jpg
83
+ uv run studio voices my-video # audition Kokoro voice blends -> out/voice-samples/
84
+ ```
85
+
86
+ Only scenes that have changed are re-rendered. Changing a paragraph of narration re-synthesises
87
+ that paragraph and nothing else, and re-times the visuals that depend on it.
88
+
89
+ ## Writing a script
90
+
91
+ ```markdown
92
+ ---
93
+ title: The Paris Commune
94
+ voice: bf_emma*0.6 + bf_isabella*0.4
95
+ speed: 0.85
96
+ ---
97
+ ## hook: Seventy-two days
98
+ > template: title
99
+
100
+ In the spring of 1871, the working class took power in a major city. {sub} This is the story of the Paris Commune.
101
+ ```
102
+
103
+ Each `## id: Chapter title` heading is a scene and a YouTube chapter. `> template:` chooses the
104
+ starting pattern for the scene file (`title`, `stat`, `quote`, `points`, `outro`, `blank`). Other `>`
105
+ lines are visual notes and are never spoken. `{name}` marks a cue that the scene can animate on,
106
+ and `{pause 1.5}` inserts a pause.
107
+
108
+ The full documentation is at <https://joemountwsws.github.io/explainer-studio/>. [AGENTS.md](AGENTS.md) is the reference for coding agents: every front-matter setting, voice choice, the rules for
109
+ authoring scenes, the render pipeline and known problems. Read [brand/frame.md](brand/frame.md)
110
+ before designing scenes.
111
+
112
+ ## Layout
113
+
114
+ | Path | What it is |
115
+ | --- | --- |
116
+ | `tools/studio/` | The Python CLI (`uv run studio …`) |
117
+ | `brand/` | House style shared by every episode: design spec, CSS, fonts, GSAP, pronunciations |
118
+ | `templates/` | Starter script, scene patterns and thumbnail |
119
+ | `episodes/<slug>/` | One HyperFrames project per video. `script.md` is the source of truth |
120
+ | `docs/` | The documentation site (MkDocs), including notes such as British ElevenLabs voices |
121
+ | `out/` | Rendered deliverables (gitignored) |
122
+
123
+ Worked examples: `episodes/demo-paris-commune` (Kokoro, 38s) and `episodes/demo-elevenlabs`
124
+ (the same script narrated by ElevenLabs).
125
+
126
+ ## Working with coding agents
127
+
128
+ The studio is designed to be driven by Claude Code or another coding agent. `AGENTS.md` (loaded
129
+ through `CLAUDE.md`) gives the agent the conventions it needs, the HyperFrames skills cover the
130
+ composition format and motion, and the `make-explainer-video` skill
131
+ (`.claude/skills/make-explainer-video/`) walks Claude through the whole workflow. Start `claude` in
132
+ the repository root and ask for a video, or run `/make-explainer-video`. See
133
+ [Working with a coding agent](https://joemountwsws.github.io/explainer-studio/coding-agents/).
@@ -0,0 +1,145 @@
1
+ /* Explainer house style, after wsws.org. Tokens mirror brand/frame.md.
2
+ `studio sync` copies this file (with fonts/ and gsap) into each episode's assets/brand/. */
3
+
4
+ @font-face { font-family: "PT Serif"; src: url("fonts/PTSerif-Regular.ttf") format("truetype"); font-weight: 400; font-style: normal; }
5
+ @font-face { font-family: "PT Serif"; src: url("fonts/PTSerif-Bold.ttf") format("truetype"); font-weight: 700; font-style: normal; }
6
+ @font-face { font-family: "PT Serif"; src: url("fonts/PTSerif-Italic.ttf") format("truetype"); font-weight: 400; font-style: italic; }
7
+ @font-face { font-family: "PT Serif"; src: url("fonts/PTSerif-BoldItalic.ttf") format("truetype"); font-weight: 700; font-style: italic; }
8
+ @font-face { font-family: "Source Sans 3"; src: url("fonts/SourceSans3.ttf") format("truetype"); font-weight: 200 900; font-style: normal; }
9
+ @font-face { font-family: "Source Sans 3"; src: url("fonts/SourceSans3-Italic.ttf") format("truetype"); font-weight: 200 900; font-style: italic; }
10
+ @font-face { font-family: "JetBrains Mono"; src: url("fonts/JetBrainsMono.ttf") format("truetype"); font-weight: 100 800; }
11
+
12
+ :root {
13
+ --paper: #ffffff;
14
+ --panel: #f4f4f4;
15
+ --ink: #231f20;
16
+ --ink-2: #333333;
17
+ --ink-soft: #555555;
18
+ --rule: #cccccc;
19
+ --red: #e71e25;
20
+ --red-wash: #ffdfdf;
21
+ --tag: #1b2129;
22
+
23
+ --font-serif: "PT Serif", Georgia, serif;
24
+ --font-sans: "Source Sans 3", "Helvetica Neue", Arial, sans-serif;
25
+ --font-mono: "JetBrains Mono", ui-monospace, monospace;
26
+
27
+ --safe-x: 192px; /* title-safe inset (10%) */
28
+ --safe-y: 108px;
29
+ --hairline: 2px solid var(--rule);
30
+ }
31
+
32
+ /* Full-bleed white page. */
33
+ .paper { position: absolute; inset: 0; background: var(--paper); }
34
+
35
+ /* Title-safe content box. Put scene content inside one of these. */
36
+ .stage {
37
+ position: absolute;
38
+ inset: var(--safe-y) var(--safe-x);
39
+ color: var(--ink-2);
40
+ font-family: var(--font-serif);
41
+ }
42
+
43
+ /* Reel (9:16, 1080x1920) content box. Platform UI covers the top ~200px, the right ~140px
44
+ (like/comment/share) and the bottom ~480px (caption, handle, audio). Burned captions sit
45
+ in the band y 1220-1440, so scene content ends by y 1180. */
46
+ .stage-reel {
47
+ position: absolute;
48
+ inset: 220px 72px 740px 72px;
49
+ color: var(--ink-2);
50
+ font-family: var(--font-serif);
51
+ }
52
+
53
+ /* Heavy ink rule + uppercase label, like the site's "TOP STORIES" head.
54
+ <div class="section-head"><div class="section-rule"></div><div class="label">History</div></div>
55
+ Draw the rule on with scaleX (transform-origin is left). */
56
+ .section-head { display: flex; flex-direction: column; gap: 18px; }
57
+ .section-rule { height: 6px; background: var(--ink); transform-origin: left center; }
58
+
59
+ .label {
60
+ font-family: var(--font-sans);
61
+ font-weight: 700;
62
+ font-size: 26px;
63
+ letter-spacing: 0.12em;
64
+ text-transform: uppercase;
65
+ color: var(--ink);
66
+ }
67
+
68
+ /* Dark category tag ("PERSPECTIVE"). */
69
+ .tag {
70
+ display: inline-block;
71
+ align-self: flex-start;
72
+ background: var(--tag);
73
+ color: #ffffff;
74
+ font-family: var(--font-sans);
75
+ font-weight: 700;
76
+ font-size: 24px;
77
+ letter-spacing: 0.12em;
78
+ text-transform: uppercase;
79
+ padding: 10px 18px 9px;
80
+ }
81
+
82
+ .headline {
83
+ font-family: var(--font-serif);
84
+ font-weight: 700;
85
+ font-size: 104px;
86
+ line-height: 1.12;
87
+ color: var(--ink);
88
+ margin: 0;
89
+ }
90
+
91
+ .standfirst {
92
+ font-family: var(--font-serif);
93
+ font-style: italic;
94
+ font-weight: 400;
95
+ font-size: 48px;
96
+ line-height: 1.35;
97
+ color: var(--ink-soft);
98
+ margin: 0;
99
+ }
100
+
101
+ .body {
102
+ font-family: var(--font-serif);
103
+ font-weight: 400;
104
+ font-size: 46px;
105
+ line-height: 1.4;
106
+ color: var(--ink-2);
107
+ margin: 0;
108
+ }
109
+
110
+ /* Source / byline / date line. */
111
+ .byline {
112
+ font-family: var(--font-sans);
113
+ font-weight: 600;
114
+ font-size: 28px;
115
+ color: var(--ink-soft);
116
+ }
117
+
118
+ /* Blockquote with red left rule. */
119
+ .quote {
120
+ border-left: 6px solid var(--red);
121
+ padding-left: 56px;
122
+ font-family: var(--font-serif);
123
+ color: var(--ink);
124
+ }
125
+
126
+ .italic { font-style: italic; }
127
+ .soft { color: var(--ink-soft); }
128
+ .red { color: var(--red); }
129
+ .sans { font-family: var(--font-sans); }
130
+ .mono { font-family: var(--font-mono); font-weight: 500; }
131
+ .hairline { height: 0; border-top: var(--hairline); }
132
+ .panel { background: var(--panel); padding: 40px 48px; }
133
+
134
+ /* Highlight wash: <span id="x-key" class="hl">phrase</span>
135
+ Sweep it in on a cue: tl.to("#x-key", { backgroundSize: "100% 82%", duration: 0.5 }, cue("key")) */
136
+ .hl {
137
+ background-image: linear-gradient(var(--red-wash), var(--red-wash));
138
+ background-repeat: no-repeat;
139
+ background-position: left 70%;
140
+ background-size: 0% 82%;
141
+ padding: 0 0.06em;
142
+ margin: 0 -0.06em;
143
+ -webkit-box-decoration-break: clone;
144
+ box-decoration-break: clone;
145
+ }