Skip to content
glebmatzPublic

About

Five-lane terminal rhythm game that auto-generates beatmaps from any audio file. Spectral onset detection, BPM autocorrelation, no servers, no song packs.

Topics

Resources

Contributing

Stars

5 stars

Watchers

0 watching

Forks

Repository files navigation

  ____                        _
 / ___|__ _ ___  ___ __ _  __| | ___
| |   / _` / __|/ __/ _` |/ _` |/ _ \
| |__| (_| \__ \ (_| (_| | (_| |  __/
 \____\__,_|___/\___\__,_|\__,_|\___|

A terminal rhythm game that plays any song you throw at it.

CI Crates.io License: MIT OR Apache-2.0 Downloads

Cascade gameplay demo
Drop in any audio file — Cascade analyses it, generates a beatmap, and you start playing.

↓ Install  ·  Quick start  ·  Controls  ·  Modifiers  ·  Practice  ·  How it works  ·  Contribute


Cascade is a five-lane rhythm game that runs entirely in your terminal. Drop in any .mp3 / .flac / .ogg / .wav and it will analyse the audio, detect onsets and beat, and generate beatmaps for four difficulties — all offline, with no server round-trip.

Because it's built on ratatui and rodio, it works in any truecolor-capable terminal; modern terminals that support the kitty keyboard protocol (Kitty, WezTerm, foot, Alacritty 0.13+) additionally unlock proper hold-note release detection.

Features

  • Works with your library — import any audio file, or a whole folder recursively; ID3/Vorbis tags are read automatically
  • Smart beatmaps — spectral-flux onset detection with sample-window-corrected timing, fractional BPM with half/double-tempo protection, onset-aligned beat grid, per-difficulty density, pitch-contour-aware lane placement for melodic onsets
  • Chords + holds + slides — up to 3-note chords, sustained holds, and slide notes that transition between lanes (Hard / Expert)
  • Rich visuals — half-block rendering with sub-pixel note motion, particle physics, a starfield that warps into streaks when the song gets loud, vignette, live spectrum bars, beat-synced receptor glow, chromatic-aberration flash on Perfect hits, a live early/late hit-error meter, waveform song-progress preview in the HUD, tear-free synchronized terminal output
  • Song preview — song select plays the hottest 20 seconds of the highlighted track, with a tiny live level meter next to it
  • Star ratings — every difficulty gets a ★ rating from peak note density, jacks, holds and slides, shown in song select and cascade song
  • Synthesized hit feedback — every judgement has its own click; menu navigation has its own sound
  • Modifiers — Hidden, Flashlight, Sudden Death, Perfect Only. Each combo gets its own best-score slot
  • Drain mode — optional survival variant where health bleeds over time; only Perfects meaningfully restore it
  • Practice mode — loop any section at any speed from 0.25× to 2.0×, perfect for drilling that one run you keep bailing on
  • Replay ghosts — every scored run records timing events; race an old run with cascade replay <run-id>
  • Results breakdown — timing-spread histogram, accuracy-over-the-song strip, early/late bias, roughest section, and miss-heavy lane immediately after a run
  • Song packs — bundle multiple .cscd share packages into one .cpack
  • Achievements — 12 unlockables for combos, full clears, mod runs
  • Stats dashboard — aggregate play history with a 30-day accuracy sparkline and activity heatmap, both in the UI and via cascade stats
  • Themes — five built-in palettes (Classic / Neon / Mono / Sunset / Ocean); switch instantly from Settings
  • Best scores — persisted per song / difficulty / mod combo
  • Calibration — built-in metronome calibrator removes audio latency drift
  • Terminal-native — no Electron, no GPU, ~10 MB binary

Install

Homebrew (macOS / Linux)

brew install glebmatz/cascade/cascade

One-line install (Linux / macOS, any shell)

curl --proto '=https' --tlsv1.2 -LsSf https://github.com/glebmatz/cascade/releases/latest/download/cascade-installer.sh | sh

PowerShell (Windows)

irm https://github.com/glebmatz/cascade/releases/latest/download/cascade-installer.ps1 | iex

Cargo

# Full compile from source
cargo install cascade-rhythm

# Prebuilt binary via cargo-binstall (much faster)
cargo binstall cascade-rhythm

From source

git clone https://github.com/glebmatz/cascade
cd cascade
cargo build --release
./target/release/cascade

Quick start

# 1. Import a song. ID3 tags are read automatically; beatmaps for all
#    4 difficulties are generated. Pass a directory to batch-import every
#    supported audio file under it.
cascade add ~/Music/my-favorite-song.mp3
cascade add ~/Music/                           # batch, recursive

# 2. Check what's imported (with best scores per difficulty).
cascade list

# 3. Launch straight into gameplay.
cascade play my-favorite-song --hard

# 4. Push yourself with modifiers. Each (difficulty + mod combo) gets
#    its own best-score slot.
cascade play my-favorite-song --hard --mods hd,sd

# Fix a typo in tags later:
cascade rename my-favorite-song --title "Numb" --artist "Linkin Park"

# Detailed stats for one song (all difficulties + mod combos):
cascade song my-favorite-song

# Track unlocks:
cascade achievements

# Aggregate play stats (top songs, 30-day accuracy sparkline, activity heatmap):
cascade stats

# Replay against a previous run:
cascade history
cascade replay 662f0f00-0004

# …or just run `cascade` for the full interactive UI.
cascade

First-time setup: calibrate audio latency

The first thing you should do on a new machine is calibrate. Audio output has 20–80 ms of platform-dependent latency and the game needs to know how much to compensate.

Main menu → Settings → Calibrate Audio

Tap Space on the beat for ~16 beats. The game takes the IQR-trimmed median of your timing errors and stores it as offset_ms. Takes 15 seconds once, and every hit after that will feel honest. The offset shifts both judging and the note highway, so notes reach the hit line exactly when you hear them.

Upgrading from 0.9 or older? Recalibrate once: input is now timestamped the moment it arrives instead of once per frame, and the calibrator measures against the same audio clock as gameplay.

Controls

Gameplay

Key Action
D F Space J K Lanes 1 – 5 (hold for hold notes)
Esc Pause
Q Quit to song select (while paused)

Menus

Key Action
↑ / ↓ or K / J Move cursor
Enter Confirm
Esc Back
Tab Cycle difficulty (song select)
s Cycle sort: Title / Artist / Recently added / BPM (song select)
m Toggle modifiers overlay: Hidden / Flashlight / Sudden Death / Perfect Only
p Toggle practice overlay: loop a section, adjust speed
/ Search (song select)
r Rename selected song (song select)
i Import audio file (song select)
x Delete song (song select)

Terminal compatibility

Cascade degrades gracefully across terminals. The table below notes the minimum level you need for each feature.

Terminal Truecolor Kitty kbd protocol Hold notes
Kitty ✅ ✅ ✅
WezTerm ✅ ✅ ✅
foot ✅ ✅ ✅
Alacritty 0.13+ ✅ ✅ ✅
iTerm2 ✅ ❌ ⚠️ tap-only
Terminal.app ✅ (Sonoma+) ❌ ⚠️ tap-only
Windows Terminal ✅ 🔄 (partial) ⚠️ tap-only
tmux passthrough passthrough depends on host

If hold notes are important to you and you're on macOS, Kitty and WezTerm are both free and excellent.

Configuration

Config lives at ~/.cascade/config.toml — edit directly, or use the in-game Settings screen.

[gameplay]
scroll_speed = 1.0        # 0.5 – 2.0
difficulty = "hard"
health_enabled = true
drain_mode = false        # continuous health drain; Perfects restore
holds_enabled = true

[keys]
lanes = ["d", "f", " ", "j", "k"]

[audio]
volume = 0.8
offset_ms = 0             # set by calibrator

[display]
theme = "classic"         # classic / neon / mono / sunset / ocean

Imported songs + generated beatmaps + best scores + play history live under ~/.cascade/songs/, ~/.cascade/scores.json, ~/.cascade/achievements.json, and ~/.cascade/play_history.json.

Modifiers

Press m in song select to toggle modifiers, or pass --mods on the CLI. Each (difficulty + mods) combination gets its own best-score slot, so a clean Hard run and a Hard + Hidden + Sudden Death run are tracked separately.

Code Name Effect
hd Hidden Notes appear only close to the hit zone.
fl Flashlight Highway is dark except a narrow band around the hit zone.
sd Sudden Death The first miss ends the run immediately.
po Perfect Only Anything below Perfect counts as a Miss.
cascade play my-favorite-song --hard --mods hd,fl

Practice mode

Stuck on one part of a song? Practice mode loops a section of your choice at a speed of your choice. Score and achievements are not saved while practising — it's for learning, not for scoreboards.

From the CLI

# Loop 1:30 → 2:00 at 70% speed until it clicks.
cascade play my-favorite-song --section 1:30-2:00 --speed 0.7

# Full song, slowed down.
cascade play my-favorite-song --speed 0.5

# Any difficulty still applies.
cascade play my-favorite-song --hard --section 1:30-2:00

From the UI

In song select, press p to open the practice overlay. Tab cycles fields, ←/→ nudges speed by 0.05, C clears, Enter applies, Esc closes. The practice badge stays visible in song select so you don't forget it's on, and mirrors in the top HUD during gameplay.

Practice mode silently disables modifiers for the run — by design, distractions are not what you want while learning a pattern.

Known limits

  • Slowing down lowers the pitch (rodio doesn't time-stretch).
  • The loop seek produces a tiny (~50 ms) audible gap — fine for learning, not designed for perfect-timing play.

Achievements

Twelve unlockable achievements ship with the game — full combos, grade milestones, and clearing songs with each modifier. The unlock list lives at ~/.cascade/achievements.json and you can see your progress with:

cascade achievements

Themes

Five palettes ship with the game and recolor lane backgrounds, notes, hit bursts, judgement splashes, and hit particles.

Slug Vibe
classic Default bright rainbow (red / green / yellow / blue / purple)
neon Cyberpunk — hot pink, cyan, electric violet
mono Grayscale, minimal, focused
sunset Warm gradient of crimson / orange / amber / rose
ocean Cool aqua / seafoam / deep blue / indigo

Pick one in Main menu → Settings → Theme. D/F cycles backward, J/K forward, Enter also advances. Changes apply instantly — no restart — and persist to ~/.cascade/config.toml under display.theme.

Custom themes

Drop a .toml file into ~/.cascade/themes/ and it shows up next to the built-ins in the Settings cycle. Built-in slugs (classic, neon, mono, sunset, ocean) are reserved — conflicting slugs are ignored. Each color is an [r, g, b] triple in 0..=255.

# ~/.cascade/themes/cherry.toml
slug = "cherry"
name = "Cherry Blossom"

# 5 lanes, in order: D / F / Space / J / K
lane_colors = [
  [250, 200, 210],
  [255, 170, 190],
  [240, 150, 180],
  [200, 120, 170],
  [160,  90, 150],
]

# Warm tint added to the highway as combo rises.
combo_heat = [255, 150, 180]

# Splash colors for Perfect / Great / Good / Miss.
judgement = [
  [255, 220, 230],
  [230, 180, 200],
  [180, 150, 170],
  [200,  80, 120],
]

# Hit-particle colors for Perfect / Great / Good.
particle = [
  [255, 220, 230],
  [230, 180, 200],
  [200, 160, 180],
]

Check that your files are valid with:

cascade themes

It lists built-ins + user themes and reports per-file issues (bad TOML, wrong array length, slug conflicts, duplicates).

Stats

Every non-practice run is logged to ~/.cascade/play_history.json. Runs now include replay timing events: note index, lane, input time, calibrated offset, judgement, and event kind. Open the Stats entry from the main menu, or run:

cascade stats

You get:

  • Totals — lifetime plays, time played, notes hit.
  • Top 5 songs — ranked by play count; title updates with renames.
  • Per-difficulty breakdown — plays, best accuracy, best score, average accuracy.
  • 30-day accuracy sparkline — daily average accuracy for the last 30 days rendered as unicode block glyphs. Blanks are days with no plays.
  • 30-day activity heatmap — play count per day in five density levels (· ░ ▒ ▓ █).
  • Achievement progress — unlocked out of total.

Practice runs are excluded — they are for learning, not for stats. Replay events make the file scale with note count, but typical libraries still stay small enough to load instantly.

Replays and results breakdown

List recent runs with replay ids:

cascade history

Then race one of them as a ghost:

cascade replay 662f0f00-0004

The ghost is drawn as small cross markers on the highway using the prior run's actual input timestamps, so early and late hits drift around the live notes instead of snapping to the beatmap. The Results screen also draws a timing-spread histogram (±120 ms, colored by judgement window) and a per-section accuracy strip across the whole song, alongside timing bias, early/late counts, the lane with the most misses, and the roughest 15-second window.

How the beatmap generator works

  1. Novelty: short-time FFT with Hann window (2048 / 512 hop), per-band log-magnitude with running-max whitening, half-wave-rectified spectral flux summed across 8 logarithmic bands. Each frame is timestamped at the hop where new audio entered the window, not at the window start, so onsets land on the sound instead of ~40 ms ahead of it.
  2. Peak picking: 95th-percentile normalization; local max within ±50 ms; must exceed median + 1.5 × MAD over a ±200 ms window.
  3. BPM: autocorrelation of the smoothed novelty signal in the 60 – 200 BPM window with a log-tempo prior centred at 130 BPM (keeps 140 – 180 BPM tracks from halving), then refined to a fractional value with parabolic interpolation on a peak at 2 – 8× the beat lag.
  4. Beat phase: comb cross-correlation at the estimated BPM, then nudged by the median offset of nearby onsets so the grid sits on the music.
  5. Notes: snapped to the phase-aligned grid only when an onset is already within 12 ms of a grid line (otherwise the onset time is kept, so a slightly-off grid can't drag notes off the beat), filtered per-difficulty by strength; lane chosen from the dominant band with repeat-avoidance hysteresis; per-peak secondary bands (≥50 % of top flux) can produce 2- or 3-note chords on harder difficulties.
  6. Holds: if the dominant band's whitened energy stays ≥75 % of its peak level for ≥1.5 beats, the note becomes a hold of that duration.

No labeled training data, no downloads, all deterministic. Timing is guarded by synthetic click-track and kick/hat regression tests in tests/generator_timing_test.rs. Beatmaps from older generator versions are regenerated automatically the first time a song is played.

Contributing

Contributions are very welcome — see CONTRIBUTING.md for the workflow, code style, and testing conventions.

Good places to start:

  • Issues tagged good first issue and help wanted
  • New particle effects, judgement animations, menu screens
  • Better onset detection (e.g. CNN-free drum transcription) — the generator is self-contained in src/beatmap/generator.rs
  • Extra terminals validated and added to the compatibility table

Sharing beatmaps (.cscd)

Cascade does not bundle audio — but you can share the beatmaps + metadata plus a download link to the original file. The recipient ends up with a fully playable song after one command:

# Author side. The URL is remembered in metadata.json so you only set it once.
cascade add ~/Music/song.mp3 --source-url https://example.com/song.mp3

# Pack everything (metadata + 4 beatmaps + sha256 of the audio) into one file.
cascade export my-song -o my-song.cscd

# Recipient side. Audio is fetched from the URL and hash-verified.
cascade import my-song.cscd

If the package has no source_url, cascade import still installs the beatmaps and tells you which audio filename to drop into the song folder manually. If a source_url is present but the SHA-256 doesn't match, the downloaded file is renamed to audio.mp3.mismatch and the import fails loudly — beatmap timings are derived from the original file, so a different encoding will misalign every note.

You can also import directly from a URL without packing:

cascade add https://example.com/song.mp3

Song packs (.cpack)

For multiple songs, export a pack:

cascade pack export "Starter Pack" song-one song-two song-three -o starter.cpack

Install it on another machine:

cascade pack import starter.cpack

A .cpack is just a JSON bundle of normal Cascade share packages. Audio is still fetched per song from each package's source_url, or reported as missing when no URL is recorded.

Cascade itself does not host any audio. Use this with files you have the right to redistribute (your own work, public-domain tracks, Creative Commons, etc.).

Roadmap

  • Online song sharing (upload beatmap JSON, not audio)
  • Replay ghosts and run breakdowns
  • Multi-song share packs
  • Multiplayer via terminal-to-terminal
  • Note editor for hand-tuning generated maps

See CHANGELOG.md for release history.

License

Cascade is dual-licensed under either of

at your option.

Contribution intent

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual-licensed as above, without any additional terms or conditions.

About

Five-lane terminal rhythm game that auto-generates beatmaps from any audio file. Spectral onset detection, BPM autocorrelation, no servers, no song packs.

Topics

Resources

Contributing

Stars

5 stars

Watchers

0 watching

Forks

Releases

Used by

Contributors

Languages