retiam 2.0.1 → 2.0.2
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.
- package/THIRD_PARTY_NOTICES.md +3 -3
- package/agent/AGENTS.md +67 -0
- package/agent/CLAUDE.md +2 -0
- package/agent/skill/SKILL.md +111 -0
- package/agent/skill/examples/AreaLesson.ts +48 -0
- package/agent/skill/examples/ExplainMotion.ts +32 -0
- package/agent/skill/examples/NarratedMotion.ts +42 -0
- package/agent/skill/examples/SecantToTangent.ts +142 -0
- package/agent/skill/references/api.md +109 -0
- package/agent/skill/references/explainers.md +121 -0
- package/agent/skill/references/quality.md +148 -0
- package/agent/skill/references/styles.md +58 -0
- package/agent/skill/references/tts.md +72 -0
- package/agent/templates/explainer/lesson.json +25 -0
- package/agent/templates/explainer/review.json +6 -0
- package/agent/templates/explainer/styles.json +92 -0
- package/bin/agent-kit.js +26 -0
- package/bin/cli.js +24 -4
- package/bin/context.js +57 -0
- package/bin/init.js +71 -12
- package/bin/lesson-project.js +30 -0
- package/bin/lesson.js +168 -0
- package/bin/lint.js +6 -6
- package/bin/snapshot-runtime.js +185 -0
- package/bin/snapshot.js +32 -225
- package/dist/THIRD_PARTY_NOTICES.md +3 -3
- package/dist/UTIF-BrQ3Ng5l.min.js +1 -0
- package/dist/{UTIF-2Ear_0Uo.js → UTIF-CN0-pEnd.js} +1 -1
- package/dist/decompress-D7LzbvPD.min.js +15 -0
- package/dist/index-3sAseSJQ.min.js +10472 -0
- package/dist/{index-B2sJBy4c.js → index-yQIdejOj.js} +15 -13
- package/dist/libs/explainer/example.d.ts +3 -0
- package/dist/libs/explainer/index.d.ts +5 -0
- package/dist/libs/explainer/index.js +134 -0
- package/dist/libs/explainer/plan.d.ts +12 -0
- package/dist/libs/explainer/plan.js +197 -0
- package/dist/libs/explainer/runtime.d.ts +24 -0
- package/dist/libs/explainer/styles.d.ts +3 -0
- package/dist/libs/explainer/types.d.ts +119 -0
- package/dist/libs/explainer/visuals.d.ts +18 -0
- package/dist/libs/tts/index.js +386 -1009
- package/dist/libs/vite/index.d.ts +28 -0
- package/dist/libs/vite/index.js +158 -0
- package/dist/{main-D_G4uIju.js → main-BTk_9jtl.js} +1 -1
- package/dist/main-lQwYAed8.min.js +5939 -0
- package/dist/msEdgeV2-BLEnvPJa.js +641 -0
- package/dist/opentype.module-B-1cYAb-.min.js +1 -0
- package/dist/retiam.es.js +1 -1
- package/dist/retiam.es.min.js +1 -0
- package/dist/src/engine/animation/Animation.d.ts +1 -0
- package/package.json +13 -8
- package/readme.md +7 -6
package/THIRD_PARTY_NOTICES.md
CHANGED
|
@@ -311,8 +311,8 @@ After dependency or asset changes, run `npm ci`, then
|
|
|
311
311
|
for packages that omit license files, reviewed README excerpts, and asset provenance are recorded in
|
|
312
312
|
`tools/third-party-sources.json` in the source repository. These internal maintenance records
|
|
313
313
|
are not needed in distributions. Generation uses local files and checked-in originals;
|
|
314
|
-
it does not execute package scripts or fetch new legal terms.
|
|
315
|
-
|
|
316
|
-
|
|
314
|
+
it does not execute package scripts or fetch new legal terms. This is a
|
|
315
|
+
maintenance command for dependency, asset, or license changes; ordinary builds,
|
|
316
|
+
publishing, and package-version changes do not require regenerating the inventory.
|
|
317
317
|
|
|
318
318
|
<!-- retiam-third-party-packages: ["@babel/code-frame@7.27.1","@babel/helper-validator-identifier@7.28.5","@dimforge/rapier2d-compat@0.20.0","@dimforge/rapier3d-compat@0.20.0","@petamoriken/float16@3.9.3","@puppeteer/browsers@1.9.1","@tensorflow/tfjs-backend-cpu@4.20.0","@tensorflow/tfjs-backend-webgl@4.20.0","@tensorflow/tfjs-backend-webgpu@4.20.0","@tensorflow/tfjs-converter@4.20.0","@tensorflow/tfjs-core@4.20.0","@tensorflow/tfjs-data@4.20.0","@tensorflow/tfjs-layers@4.20.0","@tensorflow/tfjs@4.20.0","@tootallnate/quickjs-emscripten@0.23.0","@types/dom-webcodecs@0.1.18","@types/estree@1.0.7","@types/libtess@1.2.0","@types/long@4.0.2","@types/node-fetch@2.6.13","@types/node@22.7.4","@types/offscreencanvas@2019.3.0","@types/offscreencanvas@2019.7.3","@types/prismjs@1.26.6","@types/seedrandom@2.4.34","@types/wicg-file-system-access@2020.9.8","@webgpu/types@0.1.38","@webgpu/types@0.1.40","@zappar/msdf-generator@1.2.4","agent-base@6.0.2","agent-base@7.1.3","ansi-regex@5.0.1","ansi-styles@4.3.0","argparse@1.0.10","argparse@2.0.1","as-number@1.0.0","ast-types@0.13.4","asynckit@0.4.0","axios@1.20.0","b4a@1.6.7","base64-js@1.5.1","basic-ftp@5.0.5","box3d-wasm@0.2.0","buffer-crc32@0.2.13","buffer@5.7.1","buffer@6.0.3","call-bind-apply-helpers@1.0.2","callsites@3.1.0","chalk@4.1.2","chromium-bidi@0.5.8","cli-progress@3.12.0","cliui@7.0.4","cliui@8.0.1","color-convert@2.0.1","color-name@1.1.4","combined-stream@1.0.8","comlink@4.4.2","commander@9.2.0","core-js@3.29.1","cosmiconfig@9.0.0","cross-fetch@4.0.0","data-uri-to-buffer@6.0.2","debug@4.3.4","degenerator@5.0.1","delayed-stream@1.0.0","devtools-protocol@0.0.1232444","dunder-proto@1.0.1","earcut@3.0.2","emoji-regex@8.0.0","end-of-stream@1.4.4","env-paths@2.2.1","error-ex@1.3.2","es-define-property@1.0.1","es-errors@1.3.0","es-object-atoms@1.1.2","es-set-tostringtag@2.1.0","esbuild@0.25.5","esbuild@0.28.1","escalade@3.2.0","escodegen@2.1.0","esm@3.2.25","esprima@4.0.1","estraverse@5.3.0","esutils@2.0.3","events@3.3.0","extract-zip@2.0.1","extrude-polyline@1.0.6","fast-fifo@1.3.2","fd-slicer@1.1.0","fdir@6.4.5","fdir@6.5.0","follow-redirects@1.16.0","form-data@4.0.6","function-bind@1.1.2","get-caller-file@2.0.5","get-intrinsic@1.3.0","get-proto@1.0.1","get-stream@5.2.0","get-uri@6.0.4","gl-vec2@1.3.0","gopd@1.2.0","hachure-fill@0.5.2","has-flag@4.0.0","has-symbols@1.1.0","has-tostringtag@1.0.2","hasown@2.0.4","http-proxy-agent@7.0.2","https-proxy-agent@5.0.1","https-proxy-agent@7.0.6","ieee754@1.2.1","import-fresh@3.3.1","inherits@2.0.4","ip-address@9.0.5","is-arrayish@0.2.1","is-fullwidth-code-point@3.0.0","isomorphic-ws@5.0.0","jolt-physics@1.1.0","js-tokens@4.0.0","js-yaml@4.1.0","jsbn@1.1.0","json-parse-even-better-errors@2.3.1","libtess@1.2.2","lines-and-columns@1.2.4","long@4.0.0","lru-cache@7.18.3","math-intrinsics@1.1.0","mathjax-full@3.2.2","matter-js@0.20.0","mhchemparser@4.2.1","mime-db@1.52.0","mime-types@2.1.35","mitt@3.0.1","mj-context-menu@0.6.1","mkdirp-classic@0.5.3","monaco-editor@0.45.0","mp4-muxer@5.2.2","ms@2.1.2","msedge-tts@2.0.8","nanoid@3.3.11","netmask@2.0.2","node-fetch@2.6.13","node-fetch@2.7.0","oidn-web@0.3.5","once@1.4.0","opentype.js@1.3.4","pac-proxy-agent@7.2.0","pac-resolver@7.0.1","pako@1.0.11","parent-module@1.0.1","parse-json@5.2.0","path-data-parser@0.1.0","pend@1.2.0","picocolors@1.1.1","picomatch@4.0.2","picomatch@4.0.3","planck@1.5.0","points-on-curve@0.2.0","points-on-path@0.2.1","polygon-clipping@0.15.7","polyline-miter-util@1.0.1","postcss@8.5.6","prismjs@1.30.0","progress@2.0.3","proxy-agent@6.3.1","proxy-from-env@1.1.0","proxy-from-env@2.1.0","pump@3.0.2","puppeteer-core@21.11.0","puppeteer@21.11.0","readable-stream@3.6.2","regenerator-runtime@0.13.11","require-directory@2.1.1","resolve-from@4.0.0","robust-predicates@3.0.3","rollup@4.41.1","roughjs@4.6.6","safe-buffer@5.2.1","seedrandom@3.0.5","smart-buffer@4.2.0","socks-proxy-agent@8.0.5","socks@2.8.5","source-map-js@1.2.1","source-map@0.6.1","speech-rule-engine@4.0.7","splaytree@3.2.3","sprintf-js@1.0.3","sprintf-js@1.1.3","stage-js@1.0.2","stream-browserify@3.0.0","streamx@2.22.1","string-width@4.2.3","string.prototype.codepointat@0.2.1","string_decoder@1.3.0","strip-ansi@6.0.1","supports-color@7.2.0","svg-parser@2.0.4","svg-pathdata@7.1.0","tar-fs@3.0.4","tar-stream@3.1.7","text-decoder@1.2.3","through@2.3.8","tiny-inflate@1.0.3","tinyglobby@0.2.15","tr46@0.0.3","tslib@2.8.1","tsx@4.22.4","typescript@5.4.5","unbzip2-stream@1.4.3","undici-types@6.19.8","urlpattern-polyfill@10.0.0","utif@2.0.1","util-deprecate@1.0.2","vite@6.3.6","webidl-conversions@3.0.1","webm-muxer@5.1.4","wgsl_reflect@1.2.3","whatwg-url@5.0.0","wicked-good-xpath@1.3.0","woff2-encoder@2.0.0","wrap-ansi@7.0.0","wrappy@1.0.2","ws@8.16.0","ws@8.22.0","xmldom-sre@0.1.31","y18n@5.0.8","yargs-parser@20.2.9","yargs-parser@21.1.1","yargs@16.2.0","yargs@17.7.2","yauzl@2.10.0","yerror@8.0.0"] -->
|
package/agent/AGENTS.md
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Retiam animation project
|
|
2
|
+
|
|
3
|
+
Read [.agents/skills/retiam/SKILL.md](.agents/skills/retiam/SKILL.md) when creating,
|
|
4
|
+
editing, reviewing, or rendering scenes. It links to the API notes, an executable
|
|
5
|
+
example, and the visual review criteria. This project uses Retiam's TypeScript
|
|
6
|
+
and WebGPU APIs.
|
|
7
|
+
|
|
8
|
+
- Author default-exported `Scene` subclasses in `src/scenes/` with `async run()`.
|
|
9
|
+
- Read the existing scene and `package.json` first. Honor the requested duration,
|
|
10
|
+
aspect ratio, and technical project conventions. A requested visual redesign
|
|
11
|
+
takes precedence over the previous scene's layout.
|
|
12
|
+
- The default creative direction is a **3Blue1Brown-inspired mathematical
|
|
13
|
+
explainer**, matching the Dijkstra reference: dark navy, vivid stable object
|
|
14
|
+
colors, smooth path drawing and value transfers, readable LaTeX, conversational
|
|
15
|
+
questions, thinking pauses, and one attention target at a time. Keep the
|
|
16
|
+
implementation in Retiam TypeScript/WebGPU.
|
|
17
|
+
- Educational projects include `lesson/lesson.json`. Read it before creating an
|
|
18
|
+
explainer, then replace the square-area reference with the user's topic,
|
|
19
|
+
audience, objective, sources, narration and visible events. Use the packaged
|
|
20
|
+
skill's `references/explainers.md` for the production workflow and public API.
|
|
21
|
+
- Style is independent of subject and pedagogy. Honor an explicit prompt style
|
|
22
|
+
over the saved profile, and persist it in the lesson: mathematical (default),
|
|
23
|
+
excalidraw, minimal, cinematic, or custom overrides extending a profile. Read
|
|
24
|
+
`references/styles.md` for the selected look. A biology video need not contain
|
|
25
|
+
code or a table; choose representations that explain its mechanism.
|
|
26
|
+
- For a new video, read the skill's `references/quality.md` before planning.
|
|
27
|
+
Explain through evolving geometry, simulations, and visible cause and effect.
|
|
28
|
+
Avoid a default layout of persistent text panels, chapter slides, page counters,
|
|
29
|
+
and repeated fade-ins. Plan what changes on screen and where attention moves.
|
|
30
|
+
The small setup examples demonstrate APIs; `examples/SecantToTangent.ts` shows
|
|
31
|
+
a more substantial visual explanation. Follow a slide/deck layout only when requested.
|
|
32
|
+
- Use Retiam's built-in libraries for their supported features. For generated
|
|
33
|
+
narration, use `retiam/tts`; read the skill's `references/tts.md` and
|
|
34
|
+
`examples/NarratedMotion.ts`. Use another speech generator only when the user
|
|
35
|
+
requests it. Diagnose native TTS failures instead of silently installing Python
|
|
36
|
+
TTS tools or replacing the narration pipeline.
|
|
37
|
+
- Use `retiam/explainer` for prepared speech, named cues, explicit silent pauses
|
|
38
|
+
and synchronized beats. `npm run lesson:check` validates the contract;
|
|
39
|
+
`npm run lesson:prepare` caches enabled native narration. Prepare audio before
|
|
40
|
+
locking timings. Scene handlers must animate or schedule state changes, since
|
|
41
|
+
they are built before playback. Capture prediction pauses as well as reveals.
|
|
42
|
+
- Include narration for an explainer unless the brief requests a silent video.
|
|
43
|
+
The sample disables narration only for an offline first preview; enable it and
|
|
44
|
+
prepare the requested lesson's speech as part of producing the video.
|
|
45
|
+
- Separate automated checks from observed visual and factual review. Record
|
|
46
|
+
evidence in `lesson/review.json`; a valid plan or passing lint does not establish
|
|
47
|
+
teaching quality. Keep approved audio files so visual revisions can reuse them.
|
|
48
|
+
- Use the installed version's declarations as the API authority. Run
|
|
49
|
+
`npx retiam context --json` to locate its guidance, examples, and declarations.
|
|
50
|
+
The local skill is supplied by the generator; the installed engine may be newer.
|
|
51
|
+
- Type-check with `npm run check`, then lint each changed scene with
|
|
52
|
+
`npx retiam lint src/scenes/YourScene.ts --json`. Lint is a heuristic check,
|
|
53
|
+
not proof of runtime or visual correctness. Review its warnings.
|
|
54
|
+
- Capture representative moments with
|
|
55
|
+
`npx retiam snapshot src/scenes/YourScene.ts --times 0,1,3 --json`.
|
|
56
|
+
Choose times from the actual scene, open the resulting PNGs, and fix clipping,
|
|
57
|
+
overlap, readability, and pacing before final rendering.
|
|
58
|
+
- Preview with `npm run dev`. Projects with Studio discover scenes automatically;
|
|
59
|
+
the standalone page uses the scene imported by `src/main.ts`.
|
|
60
|
+
- Render a requested video with `npx retiam render src/scenes/YourScene.ts`.
|
|
61
|
+
Check `config.js` or `retiam.config.js` and command help for output settings.
|
|
62
|
+
- Report the scene and output paths, checks performed, and any unavailable visual
|
|
63
|
+
or render checks. If WebGPU/Chrome or FFmpeg is missing, report that limitation;
|
|
64
|
+
a passing type check does not establish visual quality.
|
|
65
|
+
|
|
66
|
+
Use the project's package manager and lockfile. Keep generated outputs in
|
|
67
|
+
`outputs/`. Change Studio, build tooling, and dependencies when the task needs it.
|
package/agent/CLAUDE.md
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: retiam
|
|
3
|
+
description: Create, direct, review, and render Retiam TypeScript animations and narrated explainers, with selectable visual styles and version-matched WebGPU APIs.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Retiam scene authoring
|
|
7
|
+
|
|
8
|
+
The default creative direction is a **3Blue1Brown-inspired mathematical
|
|
9
|
+
explainer**. Lead with visual intuition: ask a concrete question, construct an
|
|
10
|
+
example, reveal why it works, then connect the geometry to notation and a wider
|
|
11
|
+
principle. Use a clean dark stage, readable mathematics, a small purposeful
|
|
12
|
+
palette, continuous transformations, and carefully timed explanation. Honor an
|
|
13
|
+
explicitly different style or palette in the user's brief. Implement this with
|
|
14
|
+
Retiam's public TypeScript/WebGPU APIs and native libraries.
|
|
15
|
+
|
|
16
|
+
Start from the user's intended explanation or visual result. Read the existing
|
|
17
|
+
scene and identify its subject, audience, duration, aspect ratio, and style.
|
|
18
|
+
For a new animation, read [references/quality.md](references/quality.md) first.
|
|
19
|
+
Sketch the visible state change, the motion revealing it, and the attention target
|
|
20
|
+
for each beat. A narration paragraph with a title is not a visual beat. Scale this
|
|
21
|
+
planning to the task; a small edit does not need a new storyboard.
|
|
22
|
+
|
|
23
|
+
## Find the API before authoring
|
|
24
|
+
|
|
25
|
+
Read [references/api.md](references/api.md) for scene lifecycle, units, styling,
|
|
26
|
+
camera framing, and common mistakes. Use
|
|
27
|
+
[examples/ExplainMotion.ts](examples/ExplainMotion.ts) only for a small API smoke
|
|
28
|
+
test. For a new explainer, study [examples/SecantToTangent.ts](examples/SecantToTangent.ts):
|
|
29
|
+
one diagram evolves through comparison, convergence, a mathematical transition,
|
|
30
|
+
and a moving generalization. Adapt its directing principles to the subject; do
|
|
31
|
+
not reuse its mathematics or visual layout for unrelated briefs. Copy an example
|
|
32
|
+
into `src/scenes/` to run it in Studio.
|
|
33
|
+
|
|
34
|
+
For an educational video, read [references/explainers.md](references/explainers.md).
|
|
35
|
+
Fresh projects include a lesson contract, native narration commands, cue timing,
|
|
36
|
+
and review evidence. The default style matches the Dijkstra reference: navy,
|
|
37
|
+
vivid stable colors, continuous drawing and transfers, conversational prediction
|
|
38
|
+
and discovery. For Excalidraw, minimal, cinematic or custom styling, read only the
|
|
39
|
+
relevant profile in [references/styles.md](references/styles.md). An explicit
|
|
40
|
+
user style overrides the saved profile; retain the teaching workflow.
|
|
41
|
+
`AreaLesson.ts` in the examples is a complete small construction using
|
|
42
|
+
`retiam/explainer`; adapt its mechanism to the actual subject.
|
|
43
|
+
|
|
44
|
+
Run `npx retiam context --json` to find documentation and TypeScript declarations
|
|
45
|
+
belonging to the engine actually executing this command. Follow exports and
|
|
46
|
+
constructor signatures there before using an unfamiliar feature. When an API
|
|
47
|
+
example differs from the installed version, follow the installed declarations
|
|
48
|
+
and release-specific setup requirements. Use the project brief and local directing
|
|
49
|
+
guide for creative decisions. Prefer public imports from `retiam` and `retiam/*`.
|
|
50
|
+
|
|
51
|
+
Choose Retiam's matching sublibrary before introducing an external implementation:
|
|
52
|
+
`retiam/tts` for generated narration and subtitles, `retiam/physics` for rigid-body
|
|
53
|
+
simulation, `retiam/charts` for charts, `retiam/graphs` for graphs and automata,
|
|
54
|
+
`retiam/shapes` for procedural shapes, `retiam/highlights` for attention effects,
|
|
55
|
+
and `retiam/rough` for sketch styling.
|
|
56
|
+
|
|
57
|
+
For voiceover, speech, or narration, read [references/tts.md](references/tts.md)
|
|
58
|
+
and [examples/NarratedMotion.ts](examples/NarratedMotion.ts) before authoring.
|
|
59
|
+
That example isolates speech integration; it is not a finished video's visual standard.
|
|
60
|
+
Use `retiam/tts` for synthesis, timing, subtitles, and the scene audio bus. Use a
|
|
61
|
+
different speech generator only when the user requests it. If Retiam TTS fails,
|
|
62
|
+
check its Vite middleware, endpoint, network access, and audio unlock; report the
|
|
63
|
+
actual blocker instead of silently installing Python TTS tools or switching providers.
|
|
64
|
+
|
|
65
|
+
## Author for the requested result
|
|
66
|
+
|
|
67
|
+
Use a default-exported class extending `Scene` with `async run()` and add visible
|
|
68
|
+
entities to the scene. Await timeline operations; use Retiam animation groups
|
|
69
|
+
for intentional concurrency. Durations and waits use milliseconds; snapshot
|
|
70
|
+
timestamps use seconds. Animate with the engine timeline, not wall-clock timers.
|
|
71
|
+
|
|
72
|
+
Build the explanation around objects that change: paths being explored, a shape
|
|
73
|
+
being decomposed, a parameter altering a field, or a construction reaching its
|
|
74
|
+
limit. Keep object identity and color meaning through transformations. Put short
|
|
75
|
+
labels near the relationship they explain; use mathematical notation for formulas.
|
|
76
|
+
Use framing, emphasis, and deliberate pauses to direct attention. Use camera moves
|
|
77
|
+
when they reveal a useful scale or relationship. Follow the default direction in
|
|
78
|
+
`references/quality.md` while honoring the brief. The construction and its timing
|
|
79
|
+
carry the explanation; extra glow, constant zooming, and decorative effects do
|
|
80
|
+
not establish this style.
|
|
81
|
+
|
|
82
|
+
Unless the user asks for slides or a dashboard, avoid a persistent title/body
|
|
83
|
+
template, a large explanatory sidebar, page counters, and screen-wide fade-out /
|
|
84
|
+
fade-in chapter changes. A table can be useful when the changing entries are the
|
|
85
|
+
subject; show it when needed and keep the visual mechanism primary. Narration
|
|
86
|
+
should explain why the visible event matters, not replace missing visual action.
|
|
87
|
+
|
|
88
|
+
## Close the feedback loop
|
|
89
|
+
|
|
90
|
+
1. Run the project's type check (`npm run check`) and
|
|
91
|
+
`npx retiam lint src/scenes/YourScene.ts --json`. Fix errors and assess warnings.
|
|
92
|
+
The linter does not replace TypeScript, execution, or image review.
|
|
93
|
+
2. Snapshot the beginning, a transition, the main explanatory moment, and the
|
|
94
|
+
resolved state using `npx retiam snapshot src/scenes/YourScene.ts --times ... --json`.
|
|
95
|
+
Use timestamps from the actual storyboard. Open the returned image paths.
|
|
96
|
+
3. Fix composition issues visible in those images. Watch playback in Studio
|
|
97
|
+
(`npm run dev`) to assess motion and pacing; still images cannot establish those.
|
|
98
|
+
Review against the presentation-pattern checks in `references/quality.md`.
|
|
99
|
+
If the middle of a beat is essentially a static slide with narration, revise
|
|
100
|
+
the visual mechanism before calling the animation finished. Intentional reading
|
|
101
|
+
holds and the resolved final frame are useful exceptions.
|
|
102
|
+
4. Render when requested with `npx retiam render src/scenes/YourScene.ts`.
|
|
103
|
+
Return artifact paths and the verification performed. State missing checks
|
|
104
|
+
explicitly when the environment cannot run them.
|
|
105
|
+
|
|
106
|
+
Snapshot requires headless Chrome with WebGPU; video rendering also requires
|
|
107
|
+
FFmpeg. `--json` returns one JSON result on stdout and diagnostics on stderr.
|
|
108
|
+
Snapshot runs the specified scene directly and advances its timeline at a fixed
|
|
109
|
+
step. Wall-clock time, unseeded randomness, live network data, and external media
|
|
110
|
+
can still make user scenes nondeterministic; use local assets and seeded data
|
|
111
|
+
when repeatable output matters.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Scene, CameraModel, CENTER, Rect, Text, Tex, CustomAnimation, Rate, hex } from 'retiam';
|
|
2
|
+
import { createExampleLesson, prepareLesson, resolveStyle, styleGeometry, drawGeometry, type LessonPlan, type PreparedSpeech } from 'retiam/explainer';
|
|
3
|
+
// lesson-config:start
|
|
4
|
+
const plan: LessonPlan = createExampleLesson();
|
|
5
|
+
const prepared: PreparedSpeech | undefined = undefined;
|
|
6
|
+
// lesson-config:end
|
|
7
|
+
/** One construction evolves through a prediction, growth, decomposition and notation. */
|
|
8
|
+
export default class AreaLesson extends Scene {
|
|
9
|
+
async run() {
|
|
10
|
+
const style = resolveStyle(plan.style);
|
|
11
|
+
this.clearColor(hex(style.background));
|
|
12
|
+
this.camera.model = CameraModel.ORTHOGRAPHIC;
|
|
13
|
+
this.camera.position.set([0, 0, 20]);
|
|
14
|
+
this.camera.orthoScale = 10;
|
|
15
|
+
const side = 2, factor = 2, total = factor * factor;
|
|
16
|
+
const cells = Array.from({ length: total }, (_, i) => {
|
|
17
|
+
const cell = styleGeometry(new Rect(side, side, { position: [-1 + (i % factor) * side, -0.8 + Math.floor(i / factor) * side, 0] }), style, i ? 'secondary' : 'primary', true);
|
|
18
|
+
if (i)
|
|
19
|
+
cell.setOpacity(0);
|
|
20
|
+
else
|
|
21
|
+
cell.setT2(0);
|
|
22
|
+
this.add(cell);
|
|
23
|
+
return cell;
|
|
24
|
+
});
|
|
25
|
+
const outline = styleGeometry(new Rect(side * factor, side * factor, { position: [0, 0.2, 0], opacity: 0 }), style, 'ink');
|
|
26
|
+
this.add(outline);
|
|
27
|
+
const question = new Text('What happens when we double a side?', { font: style.font, fontSize: 0.48, anchor: CENTER, fill: hex(style.ink), position: [0, 3.5, 0] });
|
|
28
|
+
const sideLabel = new Tex('s', { fontSize: 0.45, anchor: CENTER, fill: hex(style.primary), position: [-1, -2.25, 0] });
|
|
29
|
+
const doubled = new Tex(`${factor}s`, { fontSize: 0.45, anchor: CENTER, fill: hex(style.secondary), position: [0, -2.25, 0], opacity: 0 });
|
|
30
|
+
const equation = new Tex(`A=(${factor}s)^2=${total}s^2`, { fontSize: 0.65, anchor: CENTER, fill: hex(style.ink), position: [0, -3.25, 0], opacity: 0 });
|
|
31
|
+
const takeaway = new Text('Two growing directions, multiplied together.', { font: style.font, fontSize: 0.33, anchor: CENTER, fill: hex(style.muted), position: [0, -4.15, 0], opacity: 0 });
|
|
32
|
+
this.add(question, sideLabel, doubled, equation, takeaway);
|
|
33
|
+
const numbers = cells.map((cell, i) => { const p = cell.transform.position; const number = new Tex(String(i + 1), { fontSize: 0.48, anchor: CENTER, fill: hex(style.ink), position: [p.x, p.y, 0], opacity: 0 }); this.add(number); return number; });
|
|
34
|
+
const lesson = await prepareLesson(this, plan, { prepared });
|
|
35
|
+
await lesson.run({
|
|
36
|
+
predict: b => [drawGeometry(cells[0], style, b.cue('show'), Math.min(1800, b.durationMs - 500))],
|
|
37
|
+
grow: b => [new CustomAnimation(t => {
|
|
38
|
+
outline.setOpacity(t);
|
|
39
|
+
outline.transform.setScale([0.5 + 0.5 * t, 0.5 + 0.5 * t, 1]);
|
|
40
|
+
outline.transform.position.set([-1 + t, -0.8 + t, 0]);
|
|
41
|
+
sideLabel.setOpacity(1 - t);
|
|
42
|
+
doubled.setOpacity(t);
|
|
43
|
+
}, { delay: b.cue('expand'), duration: Math.min(2200, b.durationMs - 600), rate_func: Rate.smooth() })],
|
|
44
|
+
count: b => [new CustomAnimation(t => numbers[0].setOpacity(t), { delay: b.cue('reveal'), duration: 600 }), ...cells.slice(1).map((cell, i) => new CustomAnimation(t => { cell.setOpacity(t); numbers[i + 1].setOpacity(t); }, { delay: b.cue('reveal') + 300 + i * 850, duration: 800, rate_func: Rate.smooth() }))],
|
|
45
|
+
connect: b => [new CustomAnimation(t => { question.setOpacity(1 - t); equation.setOpacity(t); }, { delay: b.cue('equation'), duration: 1100 }), new CustomAnimation(t => takeaway.setOpacity(t), { delay: b.cue('equation') + 1600, duration: 900 })],
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CENTER, Circle, Concurrent, Create, CustomAnimation, Line, Scene, Text, Write, hex,
|
|
3
|
+
} from 'retiam';
|
|
4
|
+
|
|
5
|
+
/** A 7-second example: set the stage, move a marker, then hold the result. */
|
|
6
|
+
export default class ExplainMotion extends Scene {
|
|
7
|
+
async run() {
|
|
8
|
+
this.clearColor(hex('#080C14'));
|
|
9
|
+
this.camera.position.set([0, 0, 14]);
|
|
10
|
+
|
|
11
|
+
const title = new Text('Position changes over time', {
|
|
12
|
+
fontSize: 0.6, anchor: CENTER, fill: hex('#F8FAFC'), position: [0, 2, 0],
|
|
13
|
+
});
|
|
14
|
+
const path = new Line([[-3, 0, 0], [3, 0, 0]], {
|
|
15
|
+
stroke: hex('#64748B'), strokeWidth: 0.04,
|
|
16
|
+
});
|
|
17
|
+
const marker = new Circle(0.18, { fill: hex('#38BDF8'), position: [-3, 0, 0] });
|
|
18
|
+
this.add(title, path, marker);
|
|
19
|
+
// Include the whole path so the moving marker stays within the shot.
|
|
20
|
+
this.camera.frame([title, path, marker], { padding: 0.3 });
|
|
21
|
+
|
|
22
|
+
await this.play(new Concurrent([
|
|
23
|
+
new Write(title, { duration: 1000 }),
|
|
24
|
+
new Create(path, { duration: 1000 }),
|
|
25
|
+
]));
|
|
26
|
+
await this.wait(500);
|
|
27
|
+
await this.play(new CustomAnimation(progress => {
|
|
28
|
+
marker.transform.position.set([-3 + 6 * progress, 0, 0]);
|
|
29
|
+
}, { duration: 3500 }));
|
|
30
|
+
await this.wait(2000);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CENTER, Circle, Concurrent, Create, CustomAnimation, Line, Scene, Text, Write, hex,
|
|
3
|
+
} from 'retiam';
|
|
4
|
+
import { Speak, Subtitles } from 'retiam/tts';
|
|
5
|
+
|
|
6
|
+
export default class NarratedMotion extends Scene {
|
|
7
|
+
async run() {
|
|
8
|
+
this.clearColor(hex('#080C14'));
|
|
9
|
+
this.camera.position.set([0, 0, 14]);
|
|
10
|
+
const title = new Text('Follow the motion', {
|
|
11
|
+
fontSize: 0.6, anchor: CENTER, fill: hex('#F8FAFC'), position: [0, 2, 0],
|
|
12
|
+
});
|
|
13
|
+
const path = new Line([[-3, 0, 0], [3, 0, 0]], {
|
|
14
|
+
stroke: hex('#64748B'), strokeWidth: 0.04,
|
|
15
|
+
});
|
|
16
|
+
const marker = new Circle(0.18, { fill: hex('#38BDF8'), position: [-3, 0, 0] });
|
|
17
|
+
const subtitles = new Subtitles({ fontSize: 0.3, bottomOffset: -1.7, maxWidth: 8 });
|
|
18
|
+
this.add(title, path, marker, subtitles);
|
|
19
|
+
this.camera.frame([[-4.5, -2.5, 0], [4.5, 3, 0]], { padding: 0.25 });
|
|
20
|
+
|
|
21
|
+
const narration = new Speak('Watch the marker move from left to right.', {
|
|
22
|
+
voice: 'en-US-JennyNeural', speed: 1, subtitles, entity: marker,
|
|
23
|
+
pulseAmplitude: 0, volume: 1,
|
|
24
|
+
});
|
|
25
|
+
// Headless tools must wait for synthesis, not a promise requiring timeline steps.
|
|
26
|
+
const loading = narration.prepareAudio();
|
|
27
|
+
this.composer?.pendingTasks.push(loading);
|
|
28
|
+
await loading;
|
|
29
|
+
|
|
30
|
+
await this.play(new Concurrent([
|
|
31
|
+
new Write(title, { duration: 800 }),
|
|
32
|
+
new Create(path, { duration: 800 }),
|
|
33
|
+
], { duration: 800 }));
|
|
34
|
+
await this.play(new Concurrent([
|
|
35
|
+
narration,
|
|
36
|
+
new CustomAnimation(progress => {
|
|
37
|
+
marker.transform.position.set([-3 + 6 * progress, 0, 0]);
|
|
38
|
+
}, { duration: narration.duration }),
|
|
39
|
+
], { duration: narration.duration }));
|
|
40
|
+
await this.wait(800);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CameraModel, CENTER, Circle, Concurrent, Create, CustomAnimation, FadeIn, FadeOut,
|
|
3
|
+
Line, Rate, Scene, SDFText, Tex, Text, TransformMatchingTex, Write, hex,
|
|
4
|
+
} from 'retiam';
|
|
5
|
+
|
|
6
|
+
/** 21.5 seconds of one evolving construction; see references/quality.md for its beat map. */
|
|
7
|
+
export default class SecantToTangent extends Scene {
|
|
8
|
+
async run() {
|
|
9
|
+
const cyan = hex('#5DD9F5');
|
|
10
|
+
const amber = hex('#FFCB77');
|
|
11
|
+
const ink = hex('#EDF5FF');
|
|
12
|
+
const muted = hex('#8497AD');
|
|
13
|
+
this.clearColor(hex('#07101B'));
|
|
14
|
+
this.camera.model = CameraModel.ORTHOGRAPHIC;
|
|
15
|
+
this.camera.position.set([0, 0, 20]);
|
|
16
|
+
this.camera.orthoScale = 10.8;
|
|
17
|
+
|
|
18
|
+
const point = (x: number, y = x * x): [number, number, number] => [-2 + x * 1.2, -3 + y * 1.2, 0];
|
|
19
|
+
const curve = new Line(Array.from({ length: 181 }, (_, i) => point(-1.8 + i * 4 / 180)), {
|
|
20
|
+
stroke: cyan, strokeWidth: 0.055,
|
|
21
|
+
});
|
|
22
|
+
const axes = new Line([point(-2, 0), point(2.45, 0)], { stroke: muted, strokeWidth: 0.018 });
|
|
23
|
+
const vertical = new Line([point(0, -0.3), point(0, 4.9)], { stroke: muted, strokeWidth: 0.018 });
|
|
24
|
+
this.add(axes, vertical, curve);
|
|
25
|
+
for (const y of [1, 2, 3, 4]) {
|
|
26
|
+
const guide = new Line([point(-1.8, y), point(2.3, y)], {
|
|
27
|
+
stroke: hex('#26364A'), strokeWidth: 0.009, opacity: 0.55,
|
|
28
|
+
});
|
|
29
|
+
this.add(guide);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const question = new Text('How can one point have a slope?', {
|
|
33
|
+
fontSize: 0.56, anchor: CENTER, fill: ink, position: [0, 4.15, 0],
|
|
34
|
+
});
|
|
35
|
+
const functionLabel = new Tex('f(x)=x^2', { fontSize: 0.48, anchor: CENTER, fill: cyan, position: [-4.1, 2.2, 0] });
|
|
36
|
+
const p = new Circle(0.095, { fill: cyan });
|
|
37
|
+
const q = new Circle(0.095, { fill: amber });
|
|
38
|
+
const pLabel = new Text('P', { fontSize: 0.28, anchor: CENTER, fill: cyan });
|
|
39
|
+
const qLabel = new Text('Q', { fontSize: 0.28, anchor: CENTER, fill: amber });
|
|
40
|
+
const secant = new Line([point(0), point(1)], { stroke: amber, strokeWidth: 0.036 });
|
|
41
|
+
const dx = new Line([point(0), point(1)], { stroke: amber, strokeWidth: 0.025 });
|
|
42
|
+
const dy = new Line([point(0), point(1)], { stroke: cyan, strokeWidth: 0.025 });
|
|
43
|
+
const dxLabel = new Tex('\\Delta x=h', { fontSize: 0.27, anchor: CENTER, fill: amber });
|
|
44
|
+
const dyLabel = new Tex('\\Delta y', { fontSize: 0.27, anchor: CENTER, fill: cyan });
|
|
45
|
+
const formula = new Tex('m(a,h)=2a+h', {
|
|
46
|
+
fontSize: 0.65, anchor: CENTER, fill: ink, position: [4, 1.5, 0],
|
|
47
|
+
});
|
|
48
|
+
const readoutTitle = new SDFText('gap between the points', {
|
|
49
|
+
fontSize: 0.27, anchor: CENTER, fill: muted, position: [4, -0.35, 0],
|
|
50
|
+
});
|
|
51
|
+
const readout = new SDFText('1.40', {
|
|
52
|
+
fontSize: 0.85, anchor: CENTER, fill: amber, position: [4, -1.2, 0],
|
|
53
|
+
});
|
|
54
|
+
this.add(question, functionLabel, secant, dx, dy, p, q, pLabel, qLabel, dxLabel, dyLabel, formula, readoutTitle, readout);
|
|
55
|
+
|
|
56
|
+
const a = 0.6;
|
|
57
|
+
const setGap = (h: number) => {
|
|
58
|
+
const b = a + h;
|
|
59
|
+
const from = point(a);
|
|
60
|
+
const to = point(b);
|
|
61
|
+
const slope = a + b;
|
|
62
|
+
p.transform.position.set(from);
|
|
63
|
+
q.transform.position.set(to);
|
|
64
|
+
pLabel.transform.position.set([from[0] - 0.26, from[1] + 0.23, 0]);
|
|
65
|
+
qLabel.transform.position.set([to[0] + 0.25, to[1] + 0.23, 0]);
|
|
66
|
+
secant.setVertices([point(a - 0.55, a * a - slope * 0.55), point(b + 0.32, b * b + slope * 0.32)]);
|
|
67
|
+
dx.setVertices([from, point(b, a * a)]);
|
|
68
|
+
dy.setVertices([point(b, a * a), to]);
|
|
69
|
+
dxLabel.transform.position.set([(from[0] + to[0]) / 2, from[1] - 0.36, 0]);
|
|
70
|
+
dyLabel.transform.position.set([to[0] + 0.42, (from[1] + to[1]) / 2, 0]);
|
|
71
|
+
readout.setText(h.toFixed(2));
|
|
72
|
+
};
|
|
73
|
+
setGap(1.4);
|
|
74
|
+
|
|
75
|
+
// 0–3.1 s: establish the curve, then reveal the two-point measurement.
|
|
76
|
+
for (const entity of [p, q, pLabel, qLabel, secant, dx, dy, dxLabel, dyLabel, formula, readoutTitle, readout]) entity.setOpacity(0);
|
|
77
|
+
await this.play(new Concurrent([
|
|
78
|
+
new Create(curve), new Write(question), new Write(functionLabel),
|
|
79
|
+
], { duration: 1400 }));
|
|
80
|
+
for (const entity of [p, q, pLabel, qLabel, secant, dx, dy, dxLabel, dyLabel, formula, readoutTitle, readout]) entity.setOpacity(1);
|
|
81
|
+
await this.play(new Concurrent([
|
|
82
|
+
new FadeIn(p), new FadeIn(q), new FadeIn(pLabel), new FadeIn(qLabel),
|
|
83
|
+
new Create(secant), new Create(dx), new Create(dy), new Write(formula),
|
|
84
|
+
new FadeIn(dxLabel), new FadeIn(dyLabel), new FadeIn(readoutTitle), new FadeIn(readout),
|
|
85
|
+
], { duration: 900 }));
|
|
86
|
+
await this.wait(800);
|
|
87
|
+
|
|
88
|
+
// 3.1–6.3 s: the point follows the function; the line and its legs follow the data.
|
|
89
|
+
await this.play(new CustomAnimation(t => setGap(1.4 - 0.85 * t), {
|
|
90
|
+
duration: 3200, rate_func: Rate.smooth(),
|
|
91
|
+
}));
|
|
92
|
+
|
|
93
|
+
// 6.3–11.1 s: remove spent context, move closer, then expose the limit.
|
|
94
|
+
await this.play(new Concurrent([
|
|
95
|
+
new FadeOut(question), new FadeOut(functionLabel), new FadeOut(pLabel), new FadeOut(qLabel),
|
|
96
|
+
new CustomAnimation(t => {
|
|
97
|
+
this.camera.position.set([-0.55 * t, -0.55 * t, 20]);
|
|
98
|
+
this.camera.orthoScale = 10.8 - 1.6 * t;
|
|
99
|
+
}),
|
|
100
|
+
], { duration: 800 }));
|
|
101
|
+
await this.play(new CustomAnimation(t => {
|
|
102
|
+
const h = 0.55 - 0.515 * t;
|
|
103
|
+
setGap(h);
|
|
104
|
+
for (const entity of [dx, dy, dxLabel, dyLabel]) entity.setOpacity(1 - t);
|
|
105
|
+
q.setOpacity(1 - 0.8 * t);
|
|
106
|
+
}, { duration: 3400, rate_func: Rate.smooth() }));
|
|
107
|
+
await this.wait(600);
|
|
108
|
+
|
|
109
|
+
// 11.1–13.1 s: preserve the construction while the secant resolves to a tangent.
|
|
110
|
+
const tangent = new Line([point(a - 0.78, a * a - 2 * a * 0.78), point(a + 0.78, a * a + 2 * a * 0.78)], {
|
|
111
|
+
stroke: amber, strokeWidth: 0.065,
|
|
112
|
+
});
|
|
113
|
+
this.add(tangent);
|
|
114
|
+
const derivative = new Tex("f'(a)=2a", { fontSize: 0.85, anchor: CENTER, fill: amber });
|
|
115
|
+
derivative.transform.position.set(formula.transform.position);
|
|
116
|
+
await this.play(new Concurrent([
|
|
117
|
+
new Create(tangent), new FadeOut(secant), new FadeOut(q),
|
|
118
|
+
new FadeOut(readoutTitle), new FadeOut(readout),
|
|
119
|
+
new CustomAnimation(t => setGap(0.035 * (1 - t))),
|
|
120
|
+
new TransformMatchingTex(formula, derivative, { morphUnmatched: false, keyMap: { m: 'f' } }),
|
|
121
|
+
], { duration: 1200 }));
|
|
122
|
+
this.remove(secant, q, pLabel, qLabel, dx, dy, dxLabel, dyLabel, functionLabel);
|
|
123
|
+
readoutTitle.setText('instantaneous slope').setOpacity(1);
|
|
124
|
+
readout.setText('1.20').setOpacity(1);
|
|
125
|
+
await this.play(new Concurrent([new FadeIn(readoutTitle), new FadeIn(readout)], { duration: 800 }));
|
|
126
|
+
|
|
127
|
+
const setTangent = (x: number) => {
|
|
128
|
+
p.transform.position.set(point(x));
|
|
129
|
+
tangent.setVertices([point(x - 0.78, x * x - 2 * x * 0.78), point(x + 0.78, x * x + 2 * x * 0.78)]);
|
|
130
|
+
readout.setText((2 * x).toFixed(2));
|
|
131
|
+
};
|
|
132
|
+
// 13.1–19.5 s: generalize across negative, zero, and positive slopes.
|
|
133
|
+
await this.play(new CustomAnimation(t => {
|
|
134
|
+
setTangent(0.6 - 1.75 * t);
|
|
135
|
+
this.camera.position.set([-0.55 * (1 - t), -0.55 * (1 - t), 20]);
|
|
136
|
+
this.camera.orthoScale = 9.2 + 1.6 * t;
|
|
137
|
+
}, { duration: 2300, rate_func: Rate.smooth() }));
|
|
138
|
+
await this.play(new CustomAnimation(t => setTangent(-1.15 + 2.65 * t), { duration: 3200, rate_func: Rate.smooth() }));
|
|
139
|
+
await this.play(new CustomAnimation(t => setTangent(1.5 - 0.4 * t), { duration: 900, rate_func: Rate.smooth() }));
|
|
140
|
+
await this.wait(2000);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Retiam API notes
|
|
2
|
+
|
|
3
|
+
These notes accompany the package release. The installed `.d.ts` files are the
|
|
4
|
+
authority for exact signatures. `npx retiam context --json` locates them without
|
|
5
|
+
requiring an engine source checkout or online documentation.
|
|
6
|
+
|
|
7
|
+
## Scene lifecycle and timing
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { Scene, Circle, Create, hex } from 'retiam';
|
|
11
|
+
|
|
12
|
+
export default class MyScene extends Scene {
|
|
13
|
+
async run() {
|
|
14
|
+
this.clearColor(hex('#080C14'));
|
|
15
|
+
this.camera.position.set([0, 0, 14]);
|
|
16
|
+
const circle = new Circle(1, { stroke: hex('#38BDF8'), strokeWidth: 0.05 });
|
|
17
|
+
this.add(circle);
|
|
18
|
+
this.camera.frame(circle, { padding: 0.25 });
|
|
19
|
+
await this.play(new Create(circle, { duration: 1000 }));
|
|
20
|
+
await this.wait(800);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
`run()` schedules the animation. `scene.start(canvas)` mounts a standalone scene;
|
|
26
|
+
Studio and the snapshot CLI mount it themselves. Do not bootstrap another canvas
|
|
27
|
+
inside `run()`. Scene files default-export the class, not an instance.
|
|
28
|
+
|
|
29
|
+
`this.play(...)`, `this.wait(...)`, animation `duration`, and render CLI `-d` are
|
|
30
|
+
in **milliseconds**. Snapshot `--times` is in **seconds**.
|
|
31
|
+
For parallel animations, await `this.play(new Concurrent([animationA, animationB]))`.
|
|
32
|
+
Use `Sequential` or chained awaits for successive actions. `CustomAnimation`
|
|
33
|
+
accepts `(progress) => { ... }` and `{ duration, rate_func }`; progress is eased
|
|
34
|
+
from 0 to 1. `scene.always` callbacks receive `(time, dt)`, both in milliseconds.
|
|
35
|
+
Prefer absolute values derived from progress over accumulating per-frame offsets.
|
|
36
|
+
|
|
37
|
+
## Geometry, style, and layout
|
|
38
|
+
|
|
39
|
+
- Coordinates, radii, `strokeWidth`, and `fontSize` use world units, not CSS pixels.
|
|
40
|
+
The visible range depends on projection, camera, and aspect ratio. Explicitly
|
|
41
|
+
set text size and frame the content; text defaults may be much larger than a
|
|
42
|
+
small mathematical scene needs.
|
|
43
|
+
- `new Circle(radius, options)`, `new Rect(width, height, options)`,
|
|
44
|
+
`new Line([[x1, y1, z1], [x2, y2, z2]], options)`.
|
|
45
|
+
- `new Dot(position?, { radius, ...options })`; the first argument is a position.
|
|
46
|
+
- `new Text('Label', { fontSize: 0.5, anchor: CENTER, fill: hex('#F8FAFC') })`
|
|
47
|
+
creates vector text. `SDFText` is another text implementation. Use `Tex` for
|
|
48
|
+
LaTeX and inspect `TexOptions` for mathematical text configuration.
|
|
49
|
+
- Entity options use **`strokeWidth`** and **`zIndex`**, not `stroke_width` or
|
|
50
|
+
`z_index`. Use `fill`, `stroke`, and `opacity` for 2D paint. Use material and
|
|
51
|
+
shading APIs for 3D objects.
|
|
52
|
+
- Prefer automatic scene ordering for flat diagrams. Explicit `zIndex` values
|
|
53
|
+
affect GPU depth; inspect the output rather than assuming CSS-style layer
|
|
54
|
+
numbers will work with every projection.
|
|
55
|
+
- Position with `entity.transform.position.set([x, y, z])`; use `nextTo`, `alignTo`,
|
|
56
|
+
`VBox`, or `HBox` for related objects. Add objects or their parent group to the
|
|
57
|
+
scene. Layout alone is not scene registration.
|
|
58
|
+
- `this.camera.frame(entityOrArray, { padding: 0.25 })` fits content to the viewport.
|
|
59
|
+
Frame the planned motion envelope as well as the starting geometry. Camera
|
|
60
|
+
projection uses `camera.model` and `CameraModel`, not `projectionType` strings.
|
|
61
|
+
- The root package exports `Box`, not `Box3D`. Check constructor options for mesh
|
|
62
|
+
resolution; do not assume signatures from Three.js or Python Manim.
|
|
63
|
+
|
|
64
|
+
## Import map
|
|
65
|
+
|
|
66
|
+
| Import | Use |
|
|
67
|
+
| --- | --- |
|
|
68
|
+
| `retiam` | Scene, entities, text, cameras, animation, layout, materials, math |
|
|
69
|
+
| `retiam/shapes` | Additional procedural 2D/3D shapes |
|
|
70
|
+
| `retiam/highlights` | Circumscribe, Underline, and attention animations |
|
|
71
|
+
| `retiam/charts` | Chart entities |
|
|
72
|
+
| `retiam/graphs` | Graph and automata entities |
|
|
73
|
+
| `retiam/physics` | Physics worlds, bodies, joints, backend adapters |
|
|
74
|
+
| `retiam/tts` | Speech, narration, and subtitles |
|
|
75
|
+
| `retiam/rough` | Rough styling utilities |
|
|
76
|
+
| `retiam/vite` | Node-side Vite middleware for Retiam TTS; import in Vite config |
|
|
77
|
+
|
|
78
|
+
Follow the installed sublibrary's index declarations to its options and examples
|
|
79
|
+
before using it. Optional features can have their own asset or environment needs.
|
|
80
|
+
|
|
81
|
+
For narration, follow [tts.md](tts.md) and the checked
|
|
82
|
+
[NarratedMotion.ts](../examples/NarratedMotion.ts) example. The native TTS route is
|
|
83
|
+
already enabled by new project Vite configurations. Existing projects can add
|
|
84
|
+
`msEdgeTTSPlugin` from `retiam/vite`; see the TTS guide for the configuration and
|
|
85
|
+
failure checks. Never import the Vite plugin in browser scene code.
|
|
86
|
+
|
|
87
|
+
## Commands and failure interpretation
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
npm run check
|
|
91
|
+
npx retiam lint src/scenes/YourScene.ts --json
|
|
92
|
+
npx retiam snapshot src/scenes/YourScene.ts --times 0,1.5,3 --json
|
|
93
|
+
npx retiam render src/scenes/YourScene.ts -w 1920 -h 1080 -f 60 -o outputs/video
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Lint exits nonzero on errors; warnings need review even when `valid` is true.
|
|
97
|
+
It checks common AST patterns, not complete program semantics. A successful
|
|
98
|
+
snapshot contains the requested scene path and absolute PNG paths. Failure
|
|
99
|
+
returns `status: "error"` and a nonzero exit code. Keep stdout as JSON when
|
|
100
|
+
automating; stderr may contain browser or build diagnostics.
|
|
101
|
+
|
|
102
|
+
Render `-o` is an **output directory**, not a video filename. Check `--help` for
|
|
103
|
+
supported options; snapshot produces PNGs and has no `--format` option. Render
|
|
104
|
+
reads `config.js`, with `retiam.config.js` taking precedence; CLI flags override
|
|
105
|
+
file settings. Snapshot dimensions are controlled by its own width/height flags.
|
|
106
|
+
|
|
107
|
+
Studio discovers default-exported scenes under `src/scenes/`; reload after adding
|
|
108
|
+
a file. The standalone page at `/` follows `src/main.ts` imports. Snapshot mounts
|
|
109
|
+
the named scene directly, independent of either entrypoint.
|