Dev log

What's new on the board

Chalkboard is pre-1.0. The hosted version is live at chalkboard.studio; the pipeline is also self-hostable. There are no tagged releases yet — this log is a dated summary of what landed on main, grouped by week. For the commit-level view, see GitHub.

nothing here is stable yet — APIs, flags, and on-disk layouts still move between entries.

Apr 13 – 19, 2026
"hosted launch"
Latest on main

Chalkboard is live at chalkboard.studio. Four new app pages (generate, progress, library, video) replaced the old single-page UI; Firebase Auth gates every page with Google sign-in, email/password, and invite codes.

  • Hosted launch: chalkboard.studio live on GCP Cloud Run + domain mapping with auto-provisioned TLS. Cloudflare Web Analytics on all pages.
  • Firebase Auth: login.html with Google Sign-In, email/password, and invite-code gate. Every app page redirects to login if unauthenticated; firebase.js provides requireAuth() and authHeaders() for all API calls.
  • Generate page: rebuilt with tone/palette tag-chips, pace slider (unhurried 0.85x / steady 1.0x / brisk 1.15x), captions selector, and all fields wired to real CreateJobRequest fields (tone, theme, speed, burn_captions).
  • Progress page: live pipeline station updates over SSE, scrollable peek log, placeholder content cleared immediately after auth so the page never shows stale skeleton state.
  • Library page: real API-backed video grid; filter chips wired to ?status= param; date-grouped headers from JS; hardcoded placeholder cards removed.
  • Video detail page: player + transcript + download links, all auth-guarded.
  • Nav user dropdown: "Logged in as <name>" with a Sign Out button — click-to-open instead of sign-out-on-click, consistent across all app pages.
  • Invite-code waitlist endpoint (POST /waitlist): stores email + invite code, validates against Secret Manager, returns 200/401/409.
  • Library filter chips no longer show hardcoded counts (All 47 / Done 43 / …) — counts and category tags were placeholder-only and are now removed until the API exposes them.
  • Generate form captions default changed from "SRT + burned-in" to "SRT file only" so burn is opt-in.
  • Broken @keyframes nav-news-pulse block: a Python regex over-matched the closing brace, inserting nav-user CSS inside the keyframe rule (silently discarded by browsers). Fixed with a pattern that captures the full two-stop keyframe block before inserting after it.
  • Progress page peek scrollbox now has a fixed height instead of min-height, preventing it from expanding infinitely on long runs.
  • Docs API page (/docs/api): stylesheet referenced as ./docs.css (relative — resolved to /docs/docs.css, 404). Fixed to absolute /docs.css.
  • Stale improved.html links in nav across docs pages replaced with /guide.
Apr 10 – 12, 2026
"web-UI-first docs"

Rebuilt the marketing + docs site around the web UI instead of the CLI. Real screenshots, a 24-second timelapse of the pipeline, and a clickable lightbox on every image.

  • New docs pages: guide.html (web-UI-first walkthrough with screenshots), cli.html (flag reference, context injection, env vars), and api.html (HTTP + Python API reference with sticky ToC).
  • Clickable screenshot lightbox across the whole site, including the progress timelapse video.
  • Progress-pipeline timelapse (progress.mp4, 24s) autoplaying on the homepage and guide.
  • Homepage quickstart now lists the web UI before the CLI; pipeline diagram simplified to topic → script → fact-check → animation → validate → video.
  • Positioning softened from "self-hosted web app" to "multi-agent pipeline" — neutral wording that works for the hosted version when it ships.
  • Real web-UI screenshots replace the placeholder PNGs; EXIF/ICC metadata stripped; library shot recompressed 1.2 MB → 557 KB.
Apr 7 – 9, 2026
"library & layout checker"

Generated videos live in a real library now. The pipeline gained a pre-render layout checker that runs a headless Manim dry-run to catch off-screen and overlapping elements before we waste a full render. And there's a Claude API status dot in the nav.

  • Video library: SQLite-backed library with 4-column grid, search, sort, numbered pagination, per-page selector, per-card three-dot menu (Open / Delete), and a detail page with interactive transcript that seeks the player on click.
  • Layout checker node: ChalkboardSceneBase with per-segment bounding-box, off-screen, and timing validation. Wired between code_validator and render_trigger; failures route back to the Manim agent with a formatted violation report.
  • Job status overhaul: always-visible activity icon in the nav with a dropdown of in-progress + recent jobs, replacing the single-job pill. Multi-job localStorage, animated badge dot, per-job dismiss.
  • Claude API status dot: /api/claude-status parses status.claude.com's RSS (cached 5 min) and shows green/yellow/red in the nav with the active incident on hover.
  • New templates: howto (numbered step list with progressive reveal) and timeline (horizontal axis with dated markers).
  • AI-generated video titles: script_agent now produces a concise YouTube-style title alongside the script, flowing through manifest.json into the library.
  • Subtitle track + SRT captions served on the video detail page; download includes captions.srt.
  • Watchfiles reloader no longer restarts the server on pipeline writes to output/ or *.db, which was killing in-flight jobs.
  • Multi-column tables no longer overflow zone boundaries — manim_agent prompt now includes a bounding-box rule (right_edge = x_0 + (N - 0.5) * W).
  • Layout-checker timing tracker no longer double-counts wait() duration (Manim's wait() internally calls play(Wait(...)), causing phantom +1s per wait).
Apr 3 – 6, 2026
"the web UI"

The biggest week so far. Chalkboard grew a FastAPI server and a single-page web UI with live pipeline progress over SSE, file upload with per-type size caps, and a redesigned form split into Source Material / Style / Output sections.

  • FastAPI server (server/): POST /api/jobs, POST /api/jobs/upload (multipart), GET /api/jobs/{id}/events (SSE), job file serving with path-traversal guard. Configurable port via SERVER_PORT.
  • Web frontend: vanilla index.html with job form, live SSE progress stages, and a video player + download links on completion.
  • File upload zone with drag-and-drop (folders too), per-type caps (text 2 MB · image 5 MB · PDF 20 MB · DOCX 10 MB), 24 MB total. Accepts .bat/.ps1 too.
  • Advanced form sections: Source Material (files / URLs / GitHub repos), Style (tone, theme, template), Output (speed, QA density, burn captions, quiz).
  • Help tooltips (?) on every form field and section heading.
  • Favicon, Cloudflare Web Analytics, Google Search Console verification.
  • --effort high web search: research_agent and script_agent now scan reversed(response.content) for the text block (the web_search tool inserts ServerToolUseBlocks first). Graceful TimeoutExhausted fallback so the pipeline continues on training data if search fails.
  • config.py loads .env so the server picks up TTS_BACKEND and other vars when started via uvicorn.
  • Pipeline failures now surface in job.error instead of dying silently; render is guarded on manifest.json existing.
Mar 29 – Apr 2, 2026
"context, timeouts, templates"

Context injection arrived in three flavours — local files, URLs, and GitHub repos. A timeout + retry layer now wraps every Claude call and the Docker render. The research agent (web search via Anthropic's web_search_20250305 tool) is wired in on high effort. Plus --template, --speed, chapter markers, and SRT captions.

  • --context, --url, --github owner/repo flags. pipeline/context.py collects files (gitignore-aware via pathspec) or fetches URLs (httpx + beautifulsoup4, 100k-char cap). --yes skips the large-context confirmation for non-interactive runs.
  • --template algorithm|code|compare|howto|timeline — injects layout + visual conventions into the Manim codegen prompt.
  • --speed 0.25..4.0 — native support on OpenAI TTS, ffmpeg atempo chain for Kokoro/ElevenLabs.
  • Always-on chapter markers (FFMETADATA1 atoms in final.mp4, plus stdout list) and captions.srt. --burn-captions re-encodes with the subtitles filter.
  • --qa-density zero|normal|high for visual QA sampling rate (1 frame per 30s normal, 1 per 15s high, up to 10/20 frames respectively).
  • research_agent node: runs on effort_level=high, produces a structured brief + sources that script_agent consumes (and skips its own web_search when the brief is present).
  • pipeline/retry.py with TimeoutExhausted and api_call_with_retry; per-agent timeouts. Docker render has adaptive timeout + retry loop with RenderFailed escalation.
  • Manim codegen pitfalls: DASHED isn't a v0.20.1 constant (use DashedLine); pointer-label stacks over a descriptive line need buff≥0.85; obj.copy().next_to() inside .animate captures position at animation start — use absolute offsets.
  • Segment-accurate A/V sync: generated scenes load per-segment durations from segments.json at render time instead of using hardcoded estimates. code_validator rejects scenes with hardcoded self.wait(float).
  • OpenAI TTS duration: compute from actual PCM bytes, not the WAV header (OpenAI returns nframes=INT_MAX, which made Manim render multi-day videos).
Mar 25 – 28, 2026
"first light"

The initial pipeline ships. Topic goes in, narrated Manim video comes out — in one command.

  • LangGraph pipeline: script_agentfact_validatormanim_agentcode_validatorrender_trigger, with retry loops and escalate_to_user for unrecoverable failures.
  • TTS backends: Kokoro (local, default), OpenAI, ElevenLabs — swappable via TTS_BACKEND env var.
  • Single-command render: main.py orchestrates the pipeline, builds the Docker image on first run, runs Manim in the container, then merges audio with host-side ffmpeg (Linux AAC encoder doesn't produce QuickTime-compatible output).
  • --audience beginner|intermediate|expert, --tone casual|formal|socratic, --theme chalkboard|light|colorful.
  • --preview for fast 480p15 renders (separate preview.mp4, doesn't clobber the full HD output).
  • Post-render visual QA: samples frames, sends them to Claude with the scene source, and re-invokes manim_agent up to 2 times if it flags issues.
  • Resume via --run-id; skip render if final.mp4 already exists.

Pre-1.0. No formal releases yet — everything lands on main. The hosted version is coming; until then, run it yourself.

Full commit history on GitHub ↗