Features

How Rock N Roller solves its harder problems: local CDLC, catalog search, practice speed and section loops, Demucs stems, left-handed / inverted highway layout, and a 3D chart that stays locked to the audio — from one C + raylib codebase on macOS, Windows, and WebAssembly.

Share a section: /features/#stems, /features/#loop, /features/#sqlite, …

  1. Stack & platforms
  2. Library that stays on your disk
  3. SQLite on disk (macOS / Windows)
  4. Catalog search & query language
  5. Browser library access
  6. Multithreaded wasm
  7. Practice speed without pitch shift
  8. Section loop
  9. Stems & Demucs MLX
  10. Left-handed & string order
  11. Long songs without blowing RAM
  12. 3D highway
  13. Keeping the UI smooth
  14. What ships

Stack & platforms

The app is C17 (plus one C++17 wrapper for Signalsmith Stretch) on raylib 6.0. One CMake project builds macOS (Apple Silicon and Intel), Windows x64, and Emscripten wasm from the same commit — platform differences stay in small shims for paths, dialogs, and threading.

Heavy lifting is a short list of libraries: SQLite for state and the song catalog, an in-repo PSARC reader, vgmstream for Wwise audio (no FFmpeg), ogg/vorbis, and Signalsmith for time stretch. The UI is drawn in-app; there is no separate toolkit.

Library that stays on your disk

Opening every .psarc on every launch would be slow. Instead, a local SQLite DB caches each song's metadata (title, artist, tunings, arrangements, length) keyed by file size and mtime. Startup loads the catalog from the DB; a background rescan only re-parses files that changed. Profiles, playlists, play history, and settings live in the same database. Charts are Rocksmith's binary SNG — parsed once when you pick a song, not from XML at runtime.

SQLite on disk (macOS / Windows)

On desktop, the database is a normal file on your machine — not in the cloud, not inside the app bundle. The state directory is created on first launch; the DB file is rocknroller.db (plus SQLite WAL sidecars while the app is open).

Platform Path
macOS / Linux ~/.nicapotato-app-saved-state/rocknroller/rocknroller.db
Windows %LOCALAPPDATA%\nicapotato\rocknroller\rocknroller.db
Web Same logical path inside an IDBFS mount, persisted to the browser’s IndexedDB for this site (see browser library access)

That file holds the song scan cache, profiles, playlists, play history, library folder list, stems folder path, autoplay mode, chart layout, and per-profile cell settings (including left-handed / invert). Your PSARC and stem audio stay wherever you put them — only metadata and prefs are in the DB. Reset in Settings wipes this state, not your media files.

A large CDLC library is useless if you can only scroll it. The catalog search bar accepts plain text or a small query language that filters on the metadata already in SQLite — no reopening PSARCs.

Bare words are a global substring match (case-insensitive) across title, artist, album, year, and tuning. Column filters use field="value" and can be mixed with free text:

Clause Filters on
title="…" Song title
artist="…" Artist
album="…" Album
year="…" Year
tuning="…" Tuning (display label or raw offsets), e.g. tuning="Drop D"
instruments="…" Arrangements present: v vocals, l lead, r rhythm, b bass. Optional per-arrangement tuning: l-E Standard, b-Drop D
official="true|false" Official Rocksmith DLC vs CDLC
stems="true|false" Songs with all six external stem MP3s indexed

Inside the quotes, comma means AND and | means OR. Examples:

Clicking an album or artist card fills the bar with matching artist="…" / album="…" clauses. The same query powers the play queue when autoplay advances to the next song.

Browser library access

The browser has no filesystem, but the app needs a folder of your CDLC. The solution:

Bytes go disk → page memory only. Nothing is uploaded. This (and folder picking) is why the web build needs Chrome or Edge.

Multithreaded wasm

The web build is the same C, compiled with Emscripten as multithreaded wasm: pthreads → Web Workers sharing memory via SharedArrayBuffer. That requires a cross-origin-isolated page (COOP/COEP headers). GitHub Pages can't send those, so a same-origin coi-serviceworker injects them (one reload on first visit). The ~4 MB engine then streams from CDN; the site itself stays a thin shell.

Practice speed without pitch shift

Slowing audio naively drops pitch. Practice mode needs 0.3×–3.0× with pitch held steady. Inside the audio callback, Signalsmith Stretch time-stretches each period: pull rate × source frames, emit device-rate frames at original pitch. At 1.0× the stretcher is bypassed. The chart clock compensates for stretch latency so notes stay locked to what you hear; speed and seek reset the engine atomically.

Section loop

Rocksmith charts tag named sections (intro, verse, solo, …). Press P or the footer Loop button to loop the section under the playhead: the app seeks to that section’s start and repeats until you turn loop off. While loop is on, Enter or O jumps back to the loop start and unpauses — useful after you scrub away mid-practice.

Loop edges show on the seek bar and can be dragged to tighten the span (as long as the chart has sections). Combined with pitch-preserving speed and stem mutes, this is the usual “drill this four bars” workflow. See keybinds.

Stems & Demucs MLX

Stem mode lets you mix six isolated tracks — drums, bass, other, vocals, guitar, piano — each with its own fader (0–200%). Mute the guitar and play over the rest of the band, or solo a part to learn by ear. Separation does not run inside Rock N Roller; it is done offline with the companion macOS app Demucs MLX Audio Stemmer (Apple Silicon, Demucs htdemucs_6s by default).

Stemming workflow

  1. In Demucs MLX, add an MP3/WAV or a Rocksmith *_p.psarc (it extracts the chart’s audio first).
  2. Pick an output folder — this becomes your Rock N Roller stems root.
  3. Run separation. For Eagles_Hotel-California_v1_1_p.psarc it writes:
    {stems_root}/Eagles_Hotel-California_v1_1_p/
      drums.mp3  bass.mp3  other.mp3
      vocals.mp3  guitar.mp3  piano.mp3
  4. In Rock N Roller Settings → Choose stems folder, point at that same {stems_root}, then resync.

How they link to PSARCs: Rock N Roller takes the PSARC filename without .psarc, looks for {stems_root}/{that_name}/, and requires all six MP3s. Only then is the song marked has_stems in SQLite (searchable with stems="true"). Paths are indexed per song; playback opens six streaming MP3 decoders under one mixer that shares the same time-stretch path as normal audio. On the web, ?demo=stems loads curated stem trees into the same layout.

Left-handed & string order

Two independent highway layout flags, saved per profile and per cell size class (also bulk-set from catalog Settings):

Setting Default What it does
String order (invert) Red / low-E at top Rocksmith-style default. Inverted puts red at the bottom of the lane — useful if you prefer that visual mapping.
Left-handed Right-handed Mirrors fret positions about the neck midpoint (Rocksmith-style lefty), so open/low frets sit on the opposite side of the highway.

Toggle them in Cell Settings while playing, or use Settings → “Set all to Left-handed / Right-handed” and “Set all to Default / Inverted” for every size class at once. Details: settings.

Long songs without blowing RAM

A long chart can be huge if kept fully renderer-ready. Songs over 10 minutes keep the parsed chart in a compact cache and only expose a 3-minute window to the highway. As you near the edge (with preload that scales with practice speed), the window slides and refills. Shorter songs drop the cache after the first fill.

Audio streams similarly: OGG from disk, stems as MP3 with seek points, and long WEM-only tracks decode in the background while the chart starts. Memory stays flat for a three-minute song or a forty-minute one.

3D highway

Each instrument pane gets its own scissored 3D viewport (raylib rlgl) with an asymmetric projection so the camera can sit high without keystoning the frets. Camera modes track the active fret region on a 24-fret neck; zoom, runway grade, and lookahead are adjustable. Left-handed and invert flags remap frets and strings before draw (see above).

Notes are Rocksmith-style gems (halo / body / edge), with sustains as bendable ribbons and SDF fret labels on top. Each frame batches only the notes in the visible time window and draws them in a fixed depth order. Target is 60 FPS with four panes up.

Keeping the UI smooth

The main thread renders; workers handle library sync, song load, WEM decode, stem mixer open, and album art. They finish via atomic flags the frame loop polls — no blocking joins on the hot path. Speed, seek, and volume cross into the audio callback with atomics only, so the UI can't stall playback. The same model runs on web as the pthread pool above.

What ships

Desktop is roughly an 8 MB binary; the web package is about 1.8 MB zipped (~4 MB wasm + JS + a ~420 KB preload of fonts/shaders/logo — songs never ride along). That size is mostly “link only what Rocksmith content needs” and keep assets out of the download, not a separate size project.

Numbers move with releases. See versions or press play. Section links: #sqlite, #loop, #stems, #lefty, …