Skip to main content

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 Logo

c64cast

CI codecov PyPI Python 3.11+ License: MIT

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 .sid natively 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 (midi extra).
  • 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 (midi extra).
  • 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/.crt game 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

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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

c64cast-0.1.0.tar.gz (1.4 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

c64cast-0.1.0-py3-none-any.whl (997.0 kB view details)

Uploaded Python 3

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

Hashes for c64cast-0.1.0.tar.gz
Algorithm Hash digest
SHA256 40b32b16631d485f1d499cc5787628928c2b46fc74fea6e0b54118708b0f053f
MD5 1735e5fa9bf86f22443c9fd7b747a060
BLAKE2b-256 0678df0daec8cdf81b999f22031797421dfc5c420187a172b5282938ce75bc5b

See more details on using hashes here.

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

Hashes for c64cast-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 5c9772ffed5ac7a6ca08e9debcd1c3b939f4eb289973c48446873e453aed819a
MD5 43b2746e9ea1f0765db445b8e377513b
BLAKE2b-256 291ea96488fe7a93ff526aedec6646458be66f15afe552195ff552cd9b187d2d

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page