Lesson Animation Studio — Project Brief
Lesson Animation Studio — Project Brief
Status: APPROVED, NOT STARTED. Deferred by the user (Sep 2026): *"this is for a separate
project.. not now."* Everything needed to start cold is in this document.
Locked decisions (user, Sep 2026):
stills for approval *before* building the pipeline. (High design bar — do not build a
pipeline around a look that hasn't been approved.)
website was not completed."* Master B (futures) is an incomplete product — treat it as
secondary and do not let it drive scope or sequencing. All design-direction work, token
extraction and rollout planning should be forex-first.
0. STEP 1 DELIVERABLE — design directions (BUILT, awaiting approval)
https://animation-directions.srv1738752.hstgr.cloud — 3 directions, 22 plates, each with a
4-frame storyboard (stills) and a live motion stage, all rendered in forex's own tokens from
forex's own lesson content.
| # | Direction | Register | Default for |
|---|---|---|---|
| A | Desk Blueprint | measured, annotated schematic; left price axis; mono numerals; a live READOUT panel; cyan used only for the thing being taught | pattern, chart, formula, animatedSequence |
| B | Liquid Glass | built from the app's own parts: glass card, gradient wash, chip rail, navy→cyan rail, glow on the decisive candle | motionGraphic, liveChartDemo, simulatorEmbed, hero moments |
| C | Editorial Instructor | type-led; navy rail; the lesson's own caption as the headline; one cyan underline; chart only as a specimen | callout, proTip, socratic, openResponse (the long text) |
Recommended shape: A as default, B for hero/simulator, C for text-heavy lessons — three
registers of one system, so 57 lessons of clips do not all look like a template.
Built by directions/build.py (stdlib only) → directions/site/index.html, served from
/var/www/animation-directions on VPS port 8144 (route animation-directions.yml).
Still PNGs of every plate: .../frames/big2-*.png.
Content provenance (nothing invented): the demo section is the lesson's *real*
animatedSequence — M2 L3 "Hammer & Hanging Man", 4 steps with its own labels/captions — and the
gate plate uses that lesson's *real* quiz entry ("The lower shadow of a Hammer should be at least
…" → "2x the body length"). Formula plates use M5 L2's real Position Size formula and worked
numbers. Tokens were re-read from the live CSS (:root verified 2026-09-12), not from this doc.
Rules the directions encode (carry these into the engine):
becomes a bracketed measurement).
quiz array — no invented assessments.1. The problem it solves (evidence, not conjecture)
From the verified Master B deployment audit:
interactiveVideo slots are dead. The live bundle contains 26 entries pointing at https://www.youtube.com/watch?v=…, but the renderer builds a native
<video src={data.url}> and there is zero YouTube/iframe/embed conversion anywhere in the
build. None of the 26 has ever played.
pausePoints mastery gate is implemented but used by zero content. The renderer fullyimplements pause-at-timestamp + require-a-correct-answer-to-continue.
motionGraphic, animatedSequence, scrollyStory, liveChartDemo, simulatorEmbed, flashcard, tabbedDeepDive, chart,
pattern, divider — each has 0 content using it.
(assets/index-*.js), so changing a lesson requires a full rebuild + redeploy.
AnimatedSequencePlayer / AnimationStep model is pattern-cards, notreal animation.
Net: a rich learning feature set with its entire animation and video tier either broken or
unpopulated.
⚠️ This is MASTER B–SPECIFIC — forex is the opposite case
Verified against the live forex app (Blue Monkee Trading,
limegreen-eagle-892117.hostingersite.com). Do not generalise the Master B defects:
| Master B (futures) | Forex / Blue Monkee | |
|---|---|---|
| Video section type | interactiveVideo × 26 | videoOverview × 9 |
| Video source | YouTube watch URLs | self-hosted /media/videos/hank-*.mp4 |
| Do they play? | No — a native <video src> cannot play a watch URL | Yes — real MP4s, files present in public/media/videos/ |
| YouTube URLs in bundle | 26 (all dead) | 0 |
pausePoints | implemented in the renderer, 0 content | not present at all |
| Animation section types | defined, 0 content each | actually used — motionGraphic, animatedSequence, scrollyStory, liveChartDemo, simulatorEmbed, tabbedDeepDive (1 each) |
| Section vocabulary | 21 types | richer — also audioReadalong, socratic, application, openResponse, skipCriteria, videoOverview, mindmap, infographic, slidedeck |
So forex already solved video the right way (self-hosted MP4s, no YouTube), and its animation
slots are populated. The user's statement *"we don't use any youtube videos on the website"* is
literally true for forex and effectively true for Master B (its YouTube links are dead).
What forex still lacks — and why the Studio is still worth building for it:
animatedSequence / motionGraphic are step cards ({label, caption, durationMs}),not real animation — a carousel with SVG patterns.
they drift from lesson text over time.
Net framing per product: Master B = *repair dead video slots and fill empty animation slots*.
Forex = *upgrade step-cards to real animation, add gates to working video, and make video
generatable instead of recorded.* Both point at the same Studio.
Forex in numbers (the product we are actually targeting)
9 modules · 57 lessons · ~2,400 lines of authored content. Section usage across the live app:
| Type | Uses | Note |
|---|---|---|
text / multipleChoice / heading / callout | 220 / 177 / 154 / 49 | the backbone |
application | 33 | applied tasks — heavy, real usage |
flashcard | 24 | Master B: 0 |
keyValue / comparison / proTip | 22 / 20 / 18 | |
chart / pattern | 11 / 10 | static SVG today |
audioReadalong / mindmap / infographic / slidedeck | 10 each | rich types, genuinely used |
videoOverview | 9 | real self-hosted MP4s |
skipCriteria / formula / fillInBlank / ordering / socratic | 8 / 6 / 4 / 3 / 3 | |
animatedSequence / motionGraphic / scrollyStory / liveChartDemo / simulatorEmbed / tabbedDeepDive / openResponse | 1 each | defined, demonstrated once, essentially unused |
So forex's animation tier is a token presence, not a real one — one use each, as step cards.
Meanwhile its non-animation rich types (flashcard, application, audioReadalong, mindmap,
infographic, slidedeck) are the *actual* differentiators and are heavily used. The Studio's
job on forex is to make the animation tier match that standard.
Video assets: 9 MP4s, ~333 MB total (hank-*.mp4, 26–52 MB each).
> ⚠️ Do NOT try to replace the 9 Hank videos with animation. They are **authentic recorded
> trading sessions** — real footage of real sessions. Animation cannot substitute for that and an
> attempt would be a downgrade. The Studio's value on forex is the animation *tier* (pattern,
> chart, formula, the 1-each animation slots) plus adding the mastery gate — not replacing
> footage.
Forex's video section shape (no dedicated TS interface — inline):
{ type: "videoOverview", videoOverview: { videoUrl, title, description } }
2. What the Studio is
A separate pipeline + web app that turns lesson content into **branded, precise, gated
animated clips, and publishes them so any product can consume them without a rebuild**.
| Layer | Owns |
|---|---|
| Design system | Per-product animation theme derived from that product's own design tokens. There is no single house style — each brand animates in its own palette/type. |
| Scene library | Parameterised reusable primitives: candlestick, candle series, floor/ceiling, Fibonacci retracement, annotation callout, formula, timeline, comparison, order-ticket. |
| Scene specs | One spec per lesson section, as data (YAML/JSON): primitive, params, captions, and pause-point timings. Content, not code. |
| Render worker | manim (Docker) on the VPS. Spec → MP4. Deterministic, batchable, no per-render cost. |
| Manifest + review surface | Publishes {lesson, section, clipUrl, duration, pausePoints} plus a gallery URL to approve clips before they ship. |
| Studio UI | Browser authoring: pick a scene, edit captions/copy, set pause points, preview, render, publish. |
The strategic point
Consumers read the manifest at runtime, so publishing a clip is not a rebuild. This inverts
the current model where video is baked into the bundle and 26 slots point at unplayable URLs.
It is the same principle the user already applies everywhere else: *derive from the canonical
source at render time, never embed a stale copy.*
3. Design system requirements
The first pilot clip was rejected on design — correctly. It used generic Tailwind green/red and
manim's default LaTeX serif. The fix is grounding animation in each product's real tokens.
Master B tokens (extracted from masterb-compass/src/index.css + tailwind.config.ts):
| Token | Value |
|---|---|
--background | 0 0% 7% |
--foreground | 0 0% 92% |
--card / --secondary / --muted | 0 0% 10% / 0 0% 16% / 0 0% 14% |
--primary | 0 72% 50% — brand red |
--accent | 32 95% 55% — amber |
--border | 0 2% 20% |
--chart-green | 142 76% 45% |
--chart-red | 0 72% 50% |
--chart-amber | 32 95% 55% |
--chart-blue | 205 80% 55% |
--chart-purple | 270 60% 55% |
--glass / --glass-border | 0 0% 8% / 0.85 / 0 0% 22% |
| gradients | --gradient-primary 135° red→red, --gradient-accent amber→amber, --gradient-hero subtle |
--shadow-glow | 0 0 24px red @ 0.2 |
--radius | 0.375rem |
| fonts | Rajdhani (display), Inter, JetBrains Mono |
Forex / Blue Monkee tokens — PRIMARY TARGET (extracted from /workspace/trade-compass)
| Token | Value |
|---|---|
| background / foreground | 220 30% 98% / 222 35% 12% — LIGHT theme |
| card / popover | 0 0% 100% |
| primary | 224 84% 34% — deep navy |
| accent | 224 84% 28% |
| secondary / muted | 222 28% 94% / 220 26% 94% |
| border | 222 20% 88% |
| radius | 0.7rem |
| chart-green / -red / -amber / -blue / -purple / -cyan | 158 84% 39% / 0 72% 51% / 38 92% 50% / 217 91% 60% / 262 65% 60% / 189 100% 45% |
| glass / glass-border | 220 30% 98% / 0.85 / 222 20% 86% |
| gradients | primary navy→navy · accent cyan→navy · hero navy/cyan |
| shadow-glow | 0 0 20px navy @ 0.14 |
| fonts | Inter (sans) + DM Sans + JetBrains Mono — *not* Rajdhani |
A dark variant also exists (background: 224 55% 8%, card: 224 48% 12%,
foreground: 220 40% 95%), so the ideal theme is light-first and dark-capable.
> ⚠️ Critical: forex is light-themed and Master B is dark-themed. An animation styled
> for Master B (black canvas, red/amber accents, Rajdhani) will look wrong on forex. The two
> brands share *no* type or palette — do not reuse a theme across them.
Design implications:
for genuine formulas.
product.
as a .ttf and registered with Pango). Not present by default.
token extraction; do not reuse Master B's theme across brands.
4. Content model sketch
# specs/masterb/m1-l2-candle-anatomy.yaml
product: masterb
lesson: {module: 1, id: 2}
scene: candle_anatomy
params:
open: 0.30
close: 1.50
high: 1.90
low: -0.50
labels: [High, Closed, Opened, Low]
definitions:
- {text: "Highest price reached", token: chart-green}
- {text: "Where the period ended", token: foreground}
- {text: "Where the period started", token: chart-blue}
- {text: "Lowest price reached", token: chart-red}
beats:
- {at: 6.5, hold: 2.6}
- {at: 12.5, hold: 4.2}
pausePoints:
- atSeconds: 6.5
question: "\"Where the period ended\" refers to which point on the candle?"
options: [Opened, Closed, High, Low]
correctAnswer: Closed
Rule: reuse the lesson's own quiz questions/options for pause points. Do not invent new
assessments — pull from the existing quiz array wherever possible.
Timing rule: every pause point needs a hold. A gate landing mid-animation gives the learner
nothing to look at while answering. Holds of ~2.5–4 s per gate worked in the pilot.
5. Render pipeline
manimcommunity/manim:stable), CPU only, no GPU--user root plus a world-writable bind mount is required (the image runs non-root) — see ai-video-pipeline/references/manim-clinical-animation.md for the verified invocation and the
full pitfall list
/opt/spa-range/spa_range_server.py (SPA fallback + HTTP Range). On Hostinger/Apache, Range is native — only .htaccess SPA fallback is needed.
6. Integration model
Preferred (and the reason this is worth doing): runtime manifest fetch.
manifest.json to a host.This also repairs the 26 dead slots: they become manifest entries with real MP4 URLs, and their
pausePoints finally become reachable.
A fallback option (less good) is build-time bundling, which reintroduces the rebuild-to-change
problem.
7. Build sequencing
Forex-first. Master B (futures) is an incomplete product — do not let it drive scope.
directions in the forex/Blue Monkee tokens: light canvas, navy 224 84% 34% primary,
cyan 189 100% 45% accent, the chart-* palette, Inter/JetBrains Mono. Get approval.
Reject/iterate before any pipeline work. (Optionally include one dark-variant frame, since
forex ships a dark mode.)
scene end-to-end from a spec file, themed for forex.
pattern (10), chart (11), formula (6) and the 1-eachanimation slots first — these are the animation-shaped content. Wire the runtime integration.
Do not replace the 9 Hank session videos.
8. Already proven (reusable starting points)
| Thing | Where |
|---|---|
| Working gated clip in a real lesson | https://masterb-pilot.srv1738752.hstgr.cloud/learn/1/2 |
| Verified manim render recipe + pitfalls | ai-video-pipeline/references/manim-clinical-animation.md |
| Master B deployment reality + bundle-patch preview technique | trade-platform-builder/references/masterb-deployment-and-pilot.md |
| Scene source (pilot) | /workspace/manim-test/pilot_lesson2.py |
| Scene source (demo clips) | /workspace/manim-test/masterb_candles.py |
| SPA + Range server | /opt/spa-range/spa_range_server.py |
| Rendered clips | /workspace/masterb-pilot/videos/ (VPS), /root/manim-test/media/ (VPS) |
| Runnable lesson preview | VPS port 8142 → masterb-pilot.srv1738752.hstgr.cloud |
9. Constraints and gotchas
or elsewhere. manim renders fine because they run in Docker on the VPS.
animation. Narrative film, photoreal footage, and people belong elsewhere (existing footage
pipeline / NotebookLM). For anything the learner must *manipulate*, React wins — the platform
already has LightweightChart + drawing tools and 4 games.
Text() label read as fused in a low-res QC frame is usually a vision-model misread. Re-render at -qh with a large font size before "fixing" anything.
self.wait(). Sampling mid-fade producesfalse "missing element" / "low contrast" reports.
assets/; resolve the live chunk from index.html before grepping or you will read dead builds.
assets/ ≈ 11 MB; its legacy film sat-class-8222026.mp4 is 183 MB.avhyrrrmlcnynhddrtp.supabase.co, NXDOMAIN) still affectsMaster B and is unresolved — held to the end by the user, along with moving Master B to the
Hostinger shared account. See the trade-platform-builder reference for that plan.
10. Open questions to settle at kickoff
whether the runtime integration can use a same-origin path.)
(recommended: store a content hash in the spec and flag drift in the gallery)?
narration timing would then drive the pause points.