northnoir — the local CLI for AI-drawn video
Make AI-drawn videos from your terminal, with your own AI coding agent as the
model and your own machine as the compute. The northnoir CLI checks and
renders a North Noir scene.html (the window.SCENE contract) entirely
locally — headless Chromium paints every frame, ffmpeg encodes H.264 — with
no API keys, no North Noir account, and no cloud tokens.
Your agent (Claude Code, OpenCode, Codex, Gemini CLI, Cursor) writes the drawing code, runs the checks, looks at its own frames, fixes what is wrong, and renders the final MP4. That loop is the product.
Install
Preview status: the
northnoirpackage is not on PyPI or npm yet — registry publication is pending maintainer registry access. The tested install today is from source (below). Do not trust any third-party package using this name until this page says otherwise.
From source (isolated, puts northnoir on your PATH):
git clone https://github.com/syver777/NorthNoir.git
pipx install ./NorthNoir/packages/northnoir-cli
northnoir doctor --install # installs Chromium via Playwright, then verifies
northnoir doctor # Chromium, ffmpeg, pinned libraries (SHA256)
Alternative: pip install ./NorthNoir/packages/northnoir-cli into a Python
virtual environment you manage yourself.
The npm package is not published. bin/northnoir.js in this repository is
a thin Node shim over the Python package: it needs Python 3.10+ with
playwright, Pillow and numpy already installed, so it is not a
stand-alone npx installer and is not advertised as one.
Your host coding agent and your machine do the work: the agent's own usage/subscription terms apply to its model use, and rendering uses your local CPU and disk. NorthNoir provides no model inference through this CLI and charges nothing for local work.
Requirements: Python 3.10+, ffmpeg on PATH (brew install ffmpeg on macOS,
sudo apt install ffmpeg on Debian/Ubuntu). Chromium is installed for you by
northnoir doctor --install (Playwright). Docker is an optional transparent
fallback when a local ruvil-js-renderer:local image exists.
Quickstart
northnoir init my-film --title "My Film" # scaffold scene.html
northnoir check my-film/scene.html --still # gate 1: frame 0 + report
northnoir check my-film/scene.html # gate 2: sampled frames + quality
northnoir render my-film/scene.html -o my-film/my-film.mp4
Or hand the whole job to your agent: install the bundled skill (below), then ask for a video. The skill teaches the agent the contract and the loop.
The 4-step agent loop
- Author — write
scene.html: one self-contained page whose JavaScript paints every frame of a 1920×1080 canvas. Frame 0 already shows the finished scene. - Still check —
northnoir check scene.html --still, then viewframe-0000.png. Fix composition, scale and contrast before animating. - Motion check —
northnoir check scene.html, viewcontact-sheet.png, read the readability/collision/composition flags, fix, re-check (up to 3 improvement rounds; keep the best version). - Render —
northnoir render scene.html -o out.mp4, view the poster, verify withffprobe(1920×1080, H.264, declared duration, stereo audio).
Commands
| Command | What it does |
|---|---|
northnoir doctor [--install] [--json] |
Verify Chromium/Playwright, ffmpeg/ffprobe, and the pinned scene libraries (SHA256). --install runs the guided setup (Playwright Chromium) first. |
northnoir init <dir> [--title "..."] [--duration 5] [--fps 24] [--style <id>] [--force] [--json] |
Scaffold <dir>/scene.html (working window.SCENE starter) + <dir>/.northnoir/. |
northnoir check <scene.html> [--still] [--frames 0,12,24,...] [--out <dir>] [--json] |
Deterministic observer + quality + audio check. Writes full-res frame-NNNN.png files, contact-sheet.png, report.json (and audio.wav when the scene has audio) to <scene_dir>/.northnoir/check/ by default. Always prints JSON. |
northnoir render <scene.html> [-o <output.mp4>] [--fps 24] [--duration <sec>] [--no-audio] [--json] |
Render every frame at 1920×1080, render SCENE.audio offline (48 kHz stereo), mux H.264 + AAC. Writes <output>.poster.png and <output>.sheet.png. Always prints JSON. |
northnoir styles [--json] |
List the 31 AnidoodleDraw style ids and the RuvilMarks helpers. |
northnoir skill [install] [--install] [--json] |
Print the bundled northnoir-draw skill path; install copies it into the agent skill folders. |
northnoir --version |
Print the CLI version. |
Exit codes: 0 only when the report is ok, 1 on scene/encode failure,
2 on usage errors.
check JSON: ok, still_only, errors, quality (readability / collision /
composition + flags), cues, cue_errors, audio_ok, metrics
(deterministic, canvas size, blank/frozen frames), frame_paths,
contact_sheet_path, report_path. Quality flags are review evidence — they
do not by themselves flip ok.
render JSON: ok, output_mp4, poster_png, contact_sheet_png,
frames_rendered, duration_s, fps, has_audio, size_bytes, elapsed_s.
The window.SCENE contract (cheat-sheet)
- Canvas is 1920×1080. Declare
SCENE.fps,SCENE.frames(orSCENE.duration),SCENE.width/height, andSCENE.canvas. SCENE.renderFrame(n)is a pure function ofn: no unseeded randomness, no wall-clock reads for drawing decisions (the renderer re-seeds per frame, so renders are deterministic and re-renders are byte-identical).SCENE.init()bakes textures once and registers subjects/blockers:window.__ruvilSubjects.register(id, role, boxFn)/window.__ruvilBlockers.register(...), keeping the declarativeSCENE.subjects/SCENE.blockersmirrors in sync. Draw the main subject insidewindow.__ruvilSubjects.draw('hero', () => { ... })so the checker can measure it.SCENE.cues:[{ t, prompt, seconds }]— timed sound beats, validated against the scene duration.SCENE.audio(ctx, info): procedural audio scheduled on theOfflineAudioContextyou are given (48 kHz stereo out). Keep peaks ≤ 0.7, seed every noise source, sync hits to your cues.- Only five pinned library URLs may be loaded — every other network request is
blocked by the renderer:
https://libs.ruvil/p5.min.js,rough.min.js,simplex-noise.js,ruvil-marks.js,anidoodle-draw.js(served offline from the package, SHA256-verified bydoctor).
Kit reference
- RuvilMarks (
ruvil-marks.js):paper(ctx, o)(bake the paper ground once ininit()),wash,stipple(blue-noise dots; density = tone),hatch(spacing = tone),rng(seed)(mulberry32),hash(a, b)(boil outlines 1–3 px viahash(boilIndex, strokeId)). - AnidoodleDraw (
anidoodle-draw.js):AnidoodleDraw.draw('<id>', ctx, frame, W, H)plates one of 31 finished style scenes under your scene;AnidoodleDraw.styleslists the ids at runtime. Runnorthnoir styles.
The agent skill
northnoir skill install
copies the bundled northnoir-draw skill into .claude/skills/,
.agents/skills/, ~/.config/opencode/skills/ and ~/.claude/skills/, so
Claude Code, OpenCode, Codex, Gemini CLI and Cursor agents learn the contract,
the 4-step loop, the quality-flag fix table and the craft rules before they
draw. northnoir skill prints the bundled path.
CLI vs Studio vs MCP
| CLI (this package) | Web Studio | MCP server | |
|---|---|---|---|
| Where it runs | Your machine | North Noir cloud | Your chat app |
| The model | Your AI coding agent | The model you pick in Studio | The model you pick |
| Cost | Free, local | North Noir tokens (free plan: your own API key) | North Noir tokens |
| Best for | One drawn scene, agent-driven | Timelines, voice/music, multi-section films at scale | Drawing from Claude/Cursor chats |
Troubleshooting
doctorsays Chromium will not launch — runnorthnoir doctor --install. On Linux you may also need Playwright's system libraries:python3 -m playwright install-deps chromium(asks for sudo; the CLI never runs sudo itself).ffmpegmissing — install it with your OS package manager (above);doctorprints the exact hint for your platform.- The Node shim cannot start Python / deps missing — the npm package is
not published;
bin/northnoir.jsis a repo-only thin shim. Use the source install above (recommended), or install the pinned deps into the Python the shim finds and setNORTHNOIR_PYTHONto choose the interpreter. - A library SHA256 fails — the vendored runtime is corrupted or hand-edited.
Reinstall the package; maintainers refresh it with
tools/sync_runtime.py(single source of truth:cloudrun/ruvil-js-renderer/in the repo). - Slow renders — rendering is deterministic software rendering; heavy
full-canvas grain costs time per frame. Check
avg_ms_per_framein the check report before rendering long scenes.
Development
python3 tools/sync_runtime.py # refresh northnoir_cli/runtime from cloudrun/
python3 -m pytest tests/ -q # end-to-end suite (doctor/styles/init/check/render)
python3 northnoir_cli/cli.py doctor # or: ./bin/northnoir doctor
Packaging choice: a Python package (the engine is Python + Playwright;
Chromium and ffmpeg are external tools doctor verifies). An unpublished
thin Node shim (bin/northnoir.js, private: true in package.json) is kept
for repo use only. A compiled single binary was rejected: it would have
to bundle a Python interpreter plus Playwright plus a browser download, which
is larger, more fragile across platforms, and harder to debug than pipx.
Licenses
The CLI code is proprietary North Noir software (see pyproject.toml). The
bundled scene libraries keep their own licenses, shipped beside them in
northnoir_cli/runtime/libs/: LICENSE.p5.txt (p5.js), LICENSE.roughjs.txt
(rough.js), LICENSE.simplex-noise.txt (simplex-noise), LICENSE.anidoodle.txt
(AnidoodleDraw / RuvilMarks). Their pinned SHA256 digests are in
northnoir_cli/runtime/libs/SHA256SUMS and verified by northnoir doctor.
Metadata
Release files for northnoir 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| northnoir-1.0.0.tar.gz | 625.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| northnoir-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.3 MB
Release files / northnoir-1.0.0.tar.gz
| Download URL | northnoir-1.0.0.tar.gz |
|---|---|
| Size | 625.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b17065f902f25fedc377ca5064aa04438488782481f0e30439d9779041c05226
|
|
BLAKE2b-256 checksum How to use checksums |
8a2f421fef45f037963877257c7e7ce1a2ed97762fc93c44e2d1f5e76289f3c0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.3
|
Release files / northnoir-1.0.0-py3-none-any.whl
| Download URL | northnoir-1.0.0-py3-none-any.whl |
|---|---|
| Size | 635.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
cb896c83c8d1437c278d528dc9f3fcf221b6be52da76040ed1c2c978e8fc9c8d
|
|
BLAKE2b-256 checksum How to use checksums |
29551fdf096a5e790c49f012bee6e3d79b7d5ed6304685e4fd18f560991c0bce
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.3
|