No Notes runs a drawing session where an LLM makes an image one step at a time while a blind chorus of absurd critics interrupts between steps. It saves every frame, every command, and every line of text, then bundles the result into a self-contained gallery player HTML file.
It is adapted from Kyle Booten's Lotus Chorus Workshop, ported from creative writing to visual art-making.
- An artist model states an intention for the next drawing step.
- A chorus model speaks through one or more critic personas from
config/chorus.yaml. - The artist responds and emits drawing commands.
- No Notes renders those commands to a canvas and logs the whole exchange.
make_player.pyturns the session into a playable web page.
| path | purpose |
|---|---|
config/config.yaml |
Main control panel: models, session length, canvas backend, output settings. |
config/chorus.yaml |
The critic personas. This is the most authorial/editable file. |
config/tts.yaml |
Optional text-to-speech settings. Keep real voice IDs in config/tts.local.yaml. |
run_session.py |
Runs a full drawing session. |
make_player.py |
Builds a self-contained gallery player HTML file from a session. |
canvas.py |
Selects the active canvas backend. |
svg_canvas.py |
SVG vector backend. |
excalidraw_canvas.py |
Excalidraw-style vector backend. |
models.py |
Provider adapters for OpenAI, Anthropic, and OpenRouter. |
player_template.html |
Gallery player template. |
renderers/excalidraw/ |
Optional official Excalidraw PNG renderer. |
- Python 3.10+
- At least one LLM API key:
- OpenAI
- Anthropic
- OpenRouter
- Optional, only for the official Excalidraw renderer:
- Node.js
- Playwright Chromium
- Optional, only for macOS local TTS:
- macOS
sayandafconvertcommands
- macOS
The default config uses OpenAI and the simple paint canvas backend.
Clone the repo and enter it:
git clone https://github.com/your-username/no-notes.git
cd no-notesCreate and activate a virtual environment:
python3 -m venv .venv
source .venv/bin/activateOn Windows PowerShell, activation is usually:
.venv\Scripts\Activate.ps1Install Python dependencies:
pip install -r requirements.txtCreate your local environment file:
cp .env.example .envEdit .env and replace the placeholders with real API keys. .env is ignored
by Git and is loaded automatically by the Python scripts.
For the default config, you only need:
OPENAI_API_KEY=your-real-key
If you switch providers in config/config.yaml, set the matching key instead.
python run_session.pyWhen it finishes, it writes output to a new folder under sessions/, for
example:
sessions/session_20260604_143022
Useful options:
python run_session.py --steps 20
python run_session.py --name spider_attemptpython make_player.py sessions/session_20260604_143022This creates gallery_player.html inside the session folder. Open it in a
browser. Press space to play/pause, arrow keys to step manually, and F
for fullscreen.
Edit config/config.yaml to choose providers and models.
The artist model must support image input because it sees the canvas. Chorus models do not need image input because the chorus is deliberately blind.
Example OpenRouter artist config:
artist:
provider: openrouter
model: openai/gpt-4o
temperature: 1.0Example Anthropic artist config:
artist:
provider: anthropic
model: claude-3-5-sonnet-latest
temperature: 1.0Supported providers are implemented in models.py:
openaianthropicopenrouter
To add another provider, add a new adapter in models.py and register it in
ADAPTERS.
Set the backend in config/config.yaml:
canvas:
backend: paint # "paint" | "svg" | "excalidraw"The simplest backend. Uses Pillow to render chunky MS-Paint-style raster marks. This is the default and requires no Node setup.
Renders vector commands to SVG and PNG using CairoSVG. It supports paths, polylines, hatching, scatter, gradients, text, and optional raw SVG fragments.
canvas:
backend: svg
svg_mode: limited # "limited" | "hybrid" | "raw"Uses an Excalidraw-style scene backend. The preview renderer is Python-only;
the official renderer needs Node and Playwright.
canvas:
backend: excalidraw
renderer: preview # "preview" | "official"For the official renderer:
cd renderers/excalidraw
npm install
npx playwright install chromium
cd ../..You can build a player with inlined TTS audio:
python make_player.py sessions/session_20260604_143022 --tts --out gallery_player_tts.htmlSupported TTS providers:
# macOS system voices
python make_player.py sessions/session_20260604_143022 --tts-provider macos
# Typecast
python make_player.py sessions/session_20260604_143022 --tts-provider typecast
# Fish Audio
python make_player.py sessions/session_20260604_143022 --tts-provider fishKeep real Typecast/Fish voice IDs in config/tts.local.yaml, not in
config/tts.yaml. config/tts.local.yaml is ignored by Git and overrides the
public defaults.
The chorus lives in config/chorus.yaml. Each voice is just text. Rewrite,
add, or remove voices to change the pressure the artist experiences.
Every voice inherits a shared instruction that critiques the work, not the artist. That boundary is part of the project structure.
Useful settings in config/config.yaml:
session:
steps: 10
chorus_behavior:
voices_per_step: 3
selection: random # "random" | "all" | "escalating"Session output goes into sessions/, which is ignored by Git. A session may
contain:
frames/— rendered step imagesaction_frames/— optional command-by-command imagesfinal.pngfinal.svgorfinal.excalidraw, depending on backendtranscript.jsongallery_player.html.tts_cache/, if TTS is used
The gallery player checks for optional assets:
assets/fonts/PPMondwest-Regular.otfassets/goldframe.png
They are not required. If absent, the player falls back to system fonts and no decorative frame. Only commit replacement assets if you have redistribution rights.
No module named yamlorNo module named PIL: activate your virtualenv and runpip install -r requirements.txt.- API/authentication error: check that
.envcontains the key for the provider selected inconfig/config.yaml. - Artist output is unparseable: try a different model or lower temperature.
- The run is slow: model calls dominate runtime. Reduce
session.stepsfor quick tests. - Official Excalidraw rendering fails: use
renderer: preview, or run the Node/Playwright setup inrenderers/excalidraw/.
MIT. See LICENSE.