Turn a real Commodore 64 into a programmable display + audio device via Ultimate 64 or TeensyROM+ — video, SID-music scopes, webcam, MIDI/ASID synth, WLED, generative visuals, overlays, playlists.
Project description
c64cast
c64cast turns a real Commodore 64 — driven over the network through an
Ultimate 64 or
TeensyROM+ — into a programmable
display and audio device. It runs a playlist of scenes on the real
hardware: play videos and images, stream a live webcam, visualize SID music
on a 3-voice oscilloscope, synthesize a MIDI keyboard or an ASID stream
through the real SID chip, render reactive generative visuals, or hand the
machine over to a native game or demo. Frames from any source are quantized
in real time to a VIC-II display mode (PETSCII, MCM, hi-res bitmap, multicolor
hi-res); audio plays through the SID's $D418 DAC or the hi-fi Ultimate Audio
PCM sampler. Stackable overlays decorate any scene with scrolling text,
spectrum analyzers, clocks, weather, RSS, logos, and more — and ensemble
mode drives a whole wall of C64s at once.
Install
uv tool install 'c64cast[all]' # or: pipx install 'c64cast[all]'
c64cast --config example:hello -u u64://192.168.2.64
That puts a c64cast command on your PATH. To try it without installing
anything permanently:
uvx --from 'c64cast[all]' c64cast clip.mp4 -u u64://192.168.2.64
[all] pulls in every optional feature — video files and YouTube URLs, mic
capture, MIDI, webcam gestures, the WLED bridge, the HTTP control plane, the
config wizard. Plain uv tool install c64cast gets a much smaller core install
(no mediapipe, no yt-dlp) that still covers every generative scene, PETSCII/
bitmap rendering, SID playback, and overlays; add extras à la carte later
(uv tool install 'c64cast[video,midi]'). Extras don't accumulate, so name
every one you want in a single command.
You need a reachable Ultimate 64 or TeensyROM+ — there is no emulator path for the streaming side. On the Ultimate, enable Ultimate DMA Service under F2 → Network Settings first; c64cast prints an actionable error if it can't connect.
What do you want to do?
Every row below is a runnable, single-scene demo that ships inside the
package — pass it to --config and it loops forever until you Ctrl+C. Point
it at your hardware with -u (see Quick start).
docs/usage.md
documents every option for these scenes and overlays.
| I want to… | Try | Reference |
|---|---|---|
| Play a video (or YouTube URL) | c64cast clip.mp4 · example:scene-video |
Quick playback |
| Show a live webcam as C64 art | example:scene-webcam-petscii |
Scenes |
| Visualize a SID tune (oscilloscope) | example:scene-waveform |
Scenes |
| Play a SID from a MIDI keyboard | example:scene-midi |
Scenes |
| Stream from DeepSID / SIDFactory II | example:scene-asid |
Scenes |
| Slideshow of images | example:scene-slideshow |
Scenes |
| Generative / music-reactive visuals | example:scene-generative-plasma |
Scenes |
Run a native .prg/.crt game or demo |
example:scene-launcher |
Scenes |
| An info board (clock/weather/RSS) | example:overlay-clock |
Overlays |
| Drive multiple C64s as one video wall | example:ensemble/master |
Ensemble mode |
| Make the C64 a WLED LED matrix | example:scene-wled |
WLED bridge |
| Control c64cast from the WLED app | example:wled-control |
WLED bridge |
Run any of them with c64cast --config example:<name>, or list the whole set
with c64cast --list-examples (one demo per scene type and per overlay).
Features
Scenes — a TOML playlist runs any mix of these on the real C64, each for a set duration, with an "UP NEXT" interstitial between them:
- Video — MP4/MKV/etc. (and YouTube/other URLs via yt-dlp), soundtrack and all, keyed off the audio clock so A/V can't drift.
- Webcam — live capture quantized to any display mode in real time.
- Slideshow — still images from a directory/glob, aspect-fit.
- SID waveform — plays a
.sidnatively on the C64 (via a small player PRG, not the firmware's own runner) with a per-voice oscilloscope. Handles multi-SID tunes — up to 8 chips using the U64's UltiSIDs. - MIDI → SID — bridge a live MIDI source (USB controller, DAW) into
the real SID and visualize each voice (
midiextra). - ASID client — receive an ASID stream (DeepSID in a browser,
SIDFactory II, Plogue chipsynth C64, …) and play it on the real SID with
the same 3-voice scope (
midiextra). - Generative — ≈20 procedural sources (plasma, tunnel, fire, mandelbrot, metaballs, game of life, …), optionally music-reactive and with pixel effects (trails, pulse, RGB shift, blur).
- Launcher — hand the machine over to a native
.prg/.crtgame or demo, then reclaim it. - WLED matrix — turn the C64 into a virtual LED matrix and stream live pixels to it from LedFx / xLights (DDP or WLED realtime UDP); part of the WLED bridge below.
- Blank — a solid PETSCII canvas for title cards + overlays.
Display + audio — six VIC-II display modes (hires, hires_edges,
mhires, petscii, mcm, blank), each with its own vectorized
quantizer (≈30 fps bitmap, 50/60 fps char over a LAN). Audio plays through
the SID's lo-fi $D418 DAC (4-bit, or ≈6-7-bit via the Mahoney companding
technique) or, on the U64, the high-fidelity Ultimate Audio FPGA PCM sampler.
Overlays — stack on any compatible scene: scrolling text, marquee, RSS ticker, PETSCII spectrum analyzer, clock, weather, callsign, countdown, network info, multi-line logo, demo-scene big text, OBS Studio status.
Ensemble mode — one process drives N systems at once as a
video wall, with cross-system orchestration (e.g. a big_text message
scrolling across every screen as a single canvas).
Control surfaces — the C64 keyboard itself (C= pauses, CTRL skips,
SHIFT cycles the style), an on-C64 SPACE menu for live scene tweaks, webcam
hand gestures (vision extra), a FastAPI control plane (/pause,
/resume, /skip, /reload), MIDI CC control, and SIGHUP to reload the
config.
WLED bridge — interoperate with the WLED LED
ecosystem in three directions, all under one [wled] config section: drive
real LED matrices from the C64's SID with no microphone (audio-sync
broadcast), present c64cast as a virtual WLED device the WLED app / Home
Assistant can discover and control (effects ↔ scenes, sliders ↔ live params,
presets), and turn the C64 into a virtual LED matrix that LedFx / xLights
stream live pixels to. See
WLED bridge
for the full reference.
Quick playback — skip the config file entirely and pass media straight
on the command line: c64cast clip.mp4 tune.sid pics/ plays each in turn.
Preview + recording — an optional local window mirroring what the C64 is showing, plus recording to MP4. Both are cv2-based, so neither needs an extra.
Quick start
# "Hello world": scrolls big text across a solid canvas. Needs nothing but a
# reachable U64/TR+ — no webcam, mic, SID, or video files. Ctrl-C to exit.
c64cast --config example:hello -u u64://192.168.2.64
# Save the connection target so you never type -u again:
c64cast -u u64://192.168.2.64 --save-settings
c64cast --config example:hello
-u/--url is a scheme-aware target that picks the backend + endpoint:
u64://HOST or http(s)://HOST (Ultimate 64 / II+), tr:// (TeensyROM+ over
auto-detected USB serial), tr:///dev/cu.usbmodem* / tr://COM3 (a specific
serial device), or tr://HOST (TeensyROM+ over TCP). $C64CAST_URL is the env
fallback, and --save-settings persists it (plus the capture device and SID
model) to ~/.config/c64cast/settings.toml.
example:hello is the gentlest starting point. From there:
# Try a single feature in isolation — one demo per scene type / overlay:
c64cast --config example:scene-webcam-petscii
c64cast --config example:overlay-clock
# Build your own config the easy way — the interactive wizard walks you
# through either a single scene or a multi-scene playlist (with the "UP NEXT"
# interstitial, video interleaving, and loop control) and writes a c64cast.toml
# (needs the 'wizard' extra, included in [all]; a ./c64cast.toml is picked up
# automatically when no --config is given):
c64cast --init
# ...or by hand: the `c64cast.example` demo is a fully-annotated reference
# exercising every scene + overlay. Copy it out and edit:
c64cast --print-example c64cast.example > c64cast.toml && $EDITOR c64cast.toml
c64cast
# Validate any config + check which optional extras are installed without
# touching the C64 (skip the connectivity probe to keep it offline):
c64cast --doctor --config c64cast.toml --skip-probe
The demos ship inside the package, so example:NAME works the same from an
installed wheel, from uvx, or from a git checkout.
c64cast --list-examples prints the whole set with one-line summaries;
--print-example NAME copies one out to edit. See
c64cast/examples/README.md
for the narrative version.
c64cast -h lists every CLI flag grouped by section (connection,
quick playback, video input, audio, vision input, playlist,
introspection, debug).
Quick playback (no config file)
Pass media files/directories/globs/URLs as positional arguments to play them
once, in order, without writing a TOML (mutually exclusive with --config).
Audio is on by default; --no-audio mutes.
# A video, a SID tune, then a folder of pictures, on an Ultimate 64:
c64cast -u u64://192.168.1.64 clip.mp4 tune.sid ~/Pictures/
# A clip on a TeensyROM+ over auto-detected USB serial:
c64cast -u tr:// clip.mp4
# A YouTube URL (needs the 'yt' extra, included in [all]):
c64cast 'https://youtu.be/dQw4w9WgXcQ'
Configuration
A config is a single TOML file (--config PATH, else ./c64cast.toml,
else built-in defaults) that defines the playlist and every overridable
option. Three ways to author one, plus tooling to discover and validate it —
none of which needs the C64:
# Build one interactively (single scene or multi-scene playlist):
c64cast --init # needs the 'wizard' extra
# Discover the config surface straight from the code (always in sync):
c64cast --list-scenes # scene types
c64cast --list-overlays # overlays + their restrictions
c64cast --list-modes # display modes
c64cast --list-examples # the packaged demo configs
c64cast --describe overlay:clock # full reference for one thing
c64cast --compat # overlay × display-mode matrix
c64cast --print-schema # JSON Schema for editor autocomplete
# Validate a config (and check which extras are installed) without hardware:
c64cast --doctor --config c64cast.toml --skip-probe
The discovery output and the JSON schema are generated from the same field
metadata the loader runs on, so they can't drift from the code.
c64cast --print-example c64cast.example is the fully-annotated reference; see
docs/usage.md for
the complete config walkthrough.
Machine-local defaults — connection target, capture device, SID model — live in
~/.config/c64cast/settings.toml and apply to every run, including quick
playback; write them with --save-settings. Persisted state (DAC calibrations,
WLED and loop presets) lives under ~/.local/share/c64cast/.
Live controls
While the stream is running, you control it from the C64 keyboard itself
(c64cast polls $028D, the kernal's keyboard-modifier scratch byte, at
10 Hz):
| Key on the C64 | What it does |
|---|---|
| Commodore (C=) — tap | Pause: scene + overlays tear down, screen clears, audio stops |
| Commodore (C=) — hold 3 s while paused | Resume: re-sets-up the same scene (audio + polling threads all come back) |
| CTRL — tap while playing | Skip: advance to the next interstitial after the current frame |
| SHIFT — tap while playing | Cycle the current scene's display style (palette mode / edge variant / waveform subtune / etc.) |
The C= + CTRL chord pressed on the same poll tick prefers pause — skip is suppressed. SHIFT held alongside C= or CTRL is dropped so a thumb resting on shift doesn't phantom-cycle the style. Cycled style persists across single-scene loop iterations and across pause/resume, but resets to the configured default on a real scene boundary (multi-scene transitions construct fresh display_mode instances).
Same actions are exposed over HTTP when [control] enabled = true:
curl -X POST http://127.0.0.1:8765/pause
curl -X POST http://127.0.0.1:8765/resume
curl -X POST http://127.0.0.1:8765/skip
curl -X POST http://127.0.0.1:8765/reload # re-read config from disk
Or, on macOS and Linux, send SIGHUP to the process to trigger a config
reload from the shell. Windows has no SIGHUP, so POST /reload above is
the portable route.
Documentation
- docs/guide/ — the User's Guide: a friendly, read-in-order introduction that starts from nothing and builds up. Start at Quick Start, or download the typeset PDF for your version from the latest release.
- docs/usage.md — full config reference, scene/overlay catalog with options, suggested setups
- docs/caveats.md — known quirks (6502 emulator scope, char ROM substitution, U64 endpoint variance, licensing of SIDs / videos)
- docs/troubleshooting.md — symptom-first index for "I saw X, what now?"
- docs/extending.md — how to add a new Scene, Overlay, DisplayMode, or interstitial Background
- docs/architecture.md — per-module internals: design rationale, hardware constraints, and edge-case history. Split by topic area under docs/architecture/; the index routes each module to its notes
- CHANGELOG.md — what changed in each release
Hardware needed
One of the following:
- An Ultimate 64 — confirmed with Elite I, Elite II, Ultimate II+ cartridge, or Commodore 64 Ultimate. Best results will be obtained from using the Elite II or the Commodore 64 Ultimate. Under F2 → Network Settings, enable Ultimate DMA Service, Command Interface (TCP port 64 — the Command Interface toggle gates command dispatch even when the socket is open), and Ultimate Audio for streaming PCM audio. The REST API is used for the few operations that have no DMA equivalent.
- A TeensyROM+ Multi-Capable Cartridge for C64/128 plugged into an original Commodore 64 or one of the above modern "ultimate" equivalents.
Depending on how you use it, you'll also want some of these things:
- Any C64 video output path supported by a U64/C64.
- A webcam (any cv2-compatible USB device) for live capture scenes.
- A microphone for live audio; otherwise the audio path can sit idle or play a video's soundtrack via PyAV.
- A MIDI controller if you want to use MIDI scenes or control playlists/scenes via MIDI CC messages.
- An HDMI capture device if you want to capture output directly from a U64 or C64 equipped with a Kawari Large. Example capture devices include the Elgato Cam Link 4K or the Genki ShadowCast.
- A WLED device (or a WLED-ecosystem sender like LedFx/xLights) on the same LAN if you want to use the WLED bridge in any direction — none of this is required for the core streaming experience.
There is no software emulator path for the streaming side — c64cast
writes directly to U64 memory/registers over the Ultimate DMA Service
(TCP port 64), with REST used only for the few non-DMA operations. SID
playback is
driven by a small player PRG uploaded into C64 RAM so the real 6510
calls PLAY at IRQ time (the U64 firmware's runners:sidplay runner is
deliberately avoided because it hijacks the HDMI output with its own
UI); see
docs/caveats.md
for the PSID-only limitation.
Contributing
Bug reports, feature ideas, and pull requests are all welcome. See
CONTRIBUTING.md
for the development setup (a git checkout and uv sync --all-extras), the
make check gate, and the conventions this repo follows. Security reports go
through SECURITY.md
rather than a public issue.
Acknowledgments
- Gideon Zweijtzer for the Ultimate 64 hardware and firmware.
- Travis Smith for the TeensyROM+ - including cartridge, firmware, hands-on testing, and suggestions.
- Bo Zimmerman for his excellent online and physical collections of all things Commodore.
- The HVSC team for the SID archive and the Songlengths database.
- Pex 'Mahoney' Tufvesson for the 8-bit
$D418DAC technique (his "Musings in the Key of C64" white paper) behind the optionaldac_curve = "mahoney_ultisid"audio path. - Jürgen Wothke (webSID / Tiny'R'Sid) for
documenting the
$D418filter-bit "almost 8-bit" playback approach behind Mahoney's technique. - Antonio Savona for the
48 kHz
$D418write-up. - CodeBase64 for the extensive reference material.
- Many open source contributors for all of the many Python packages that make this app possible. <3
License
MIT — see LICENSE.
Third-party assets. The User's Guide is typeset in two fonts that are
redistributed in this repository under the SIL Open Font
License 1.1, not under MIT: Jost*
(Copyright 2020 The Jost Project Authors) and Inconsolata (Copyright 2006
The Inconsolata Project Authors). They live in
docs/guide/fonts/
alongside their licence texts — see
that directory's README
for provenance and for what has to travel with them.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file c64cast-0.1.0.tar.gz.
File metadata
- Download URL: c64cast-0.1.0.tar.gz
- Upload date:
- Size: 1.4 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.12.0 {"installer":{"name":"uv","version":"0.12.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
40b32b16631d485f1d499cc5787628928c2b46fc74fea6e0b54118708b0f053f
|
|
| MD5 |
1735e5fa9bf86f22443c9fd7b747a060
|
|
| BLAKE2b-256 |
0678df0daec8cdf81b999f22031797421dfc5c420187a172b5282938ce75bc5b
|
File details
Details for the file c64cast-0.1.0-py3-none-any.whl.
File metadata
- Download URL: c64cast-0.1.0-py3-none-any.whl
- Upload date:
- Size: 997.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.12.0 {"installer":{"name":"uv","version":"0.12.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5c9772ffed5ac7a6ca08e9debcd1c3b939f4eb289973c48446873e453aed819a
|
|
| MD5 |
43b2746e9ea1f0765db445b8e377513b
|
|
| BLAKE2b-256 |
291ea96488fe7a93ff526aedec6646458be66f15afe552195ff552cd9b187d2d
|