Skip to content
pnn64Public

About

DeadSync is an ITG/StepMania engine with Vulkan/OpenGL backends, focused on perfect sync and competitive-level performance.

Resources

Stars

54 stars

Watchers

2 watching

Forks

Repository files navigation

DeadSync

DeadSync, as in "dead on sync", is a StepMania/ITG engine with Vulkan/OpenGL backends, focused on perfect sync and competitive-level performance. Cross-platform: Windows / Linux / BSD / macOS – x86_64 & ARM64.

⚠️ DeadSync is under heavy development. Bugs are expected, features may change, and things *will* break. You have been warned.

Prerequisites

Before building, ensure you have the following installed on your system:

  1. Rust: Install via rustup.
  2. Vulkan SDK: Download and install the SDK for your operating system from the LunarG website.

Windows build dependencies

Windows 7 builds

Use the Windows 7 target so unsupported Windows APIs are excluded:

rustup toolchain install nightly --component rust-src
cargo +nightly build -Z build-std=std,panic_abort --release --locked --target x86_64-win7-windows-msvc --bin deadsync
scripts/check-win7-imports.ps1 -Path target/x86_64-win7-windows-msvc/release/deadsync.exe

For 32-bit builds, substitute i686-win7-windows-msvc. The import check uses dumpbin from the Visual Studio C++ tools and also runs before Windows 7 releases are packaged. It checks for WinRT startup dependencies; testing on Windows 7 is still needed to verify runtime compatibility.

Bluetooth heart-rate monitors are unavailable in Windows 7 builds because their Windows backend requires Windows 10 or later.

Linux build dependencies (Ubuntu/Debian)

sudo apt update
sudo apt install --no-install-recommends build-essential clang cmake pkg-config libclang-dev libdbus-1-dev libudev-dev libasound2-dev libpipewire-0.3-dev libvulkan-dev libgl1-mesa-dev

macOS build dependencies (Homebrew)

Install the toolchain and native libraries:

xcode-select --install          # C/C++ toolchain, Git, Python (needed by shaderc-sys)
brew install cmake vulkan-loader molten-vk
  • CMake — required to build shaderc-sys (the shader compiler) from source.
  • vulkan-loader — the libvulkan the engine links against and loads at runtime.
  • molten-vk — the Vulkan-to-Metal driver that makes Vulkan actually run on Apple GPUs.
  • The Xcode Command Line Tools provide the compiler/linker plus the Git and Python that shaderc-sys invokes during its build.

DeadSync also includes a direct native Metal renderer that does not use wgpu. Choose Metal under Graphics > Video Renderer to use it, or choose Metal (wgpu) to keep using wgpu's Metal backend. The native renderer itself needs only Apple's system Metal framework; the Homebrew Vulkan packages above remain necessary while the Vulkan backend is part of the default build.

You do not need the LunarG Vulkan SDK for a normal build/run on macOS — the Homebrew loader + MoltenVK are enough. Install the SDK only if you want the Vulkan validation layers for debugging (GfxDebug in deadsync.ini).

Building and running

The repo ships a .cargo/config.toml that adds the Homebrew Vulkan loader to the link and runtime search paths for macOS, so no manual environment variables are required on either Apple Silicon (/opt/homebrew) or Intel (/usr/local):

cargo run                       # fast dev build
cargo run --profile local       # optimized, but faster to link than full release
cargo build --release           # fully optimized (LTO) build for benchmarking/play

Before the first run, grant Input Monitoring to your terminal (see the macOS run note under Getting Started) or the game won't receive keystrokes.

If you use a non-standard Homebrew prefix and linking still fails with library 'vulkan' not found, export the paths for your prefix:

brew_prefix="$(brew --prefix)"
export LIBRARY_PATH="$brew_prefix/lib:$brew_prefix/opt/vulkan-loader/lib:${LIBRARY_PATH:-}"
export DYLD_FALLBACK_LIBRARY_PATH="$brew_prefix/lib:$brew_prefix/opt/vulkan-loader/lib:${DYLD_FALLBACK_LIBRARY_PATH:-}"
export RUSTFLAGS="-L native=$brew_prefix/lib -L native=$brew_prefix/opt/vulkan-loader/lib"

BSD build dependencies (FreeBSD)

pkg install cmake python3 pkgconf alsa-lib alsa-plugins vulkan-validation-layers hidapi

Test fixtures

Test fixtures live in deadsync-tests, mounted as a Git submodule at tests/fixtures. Initialize the pinned fixture revision before compiling or running tests, examples, or benchmarks (Python 3.11 or newer is required to restore split archives):

git submodule update --init tests/fixtures
python scripts/prepare_test_fixtures.py

Normal game builds do not need the fixture submodule.

Archives over 50 MB are stored as parts of at most 48,000,000 bytes. The Python command verifies each part and restores the exact archives locally; restored files are ignored by Git. Run both commands again after fixture updates.

When updating fixtures, commit and push changes in tests/fixtures first, then commit the updated submodule pointer in this repository. Test runners and fixture maintenance scripts stay in this repository; their existing paths still work.

Full song Lua tests

Run a full song Lua fixture with live progress and a final comparison tally:

cargo test --test full_song_lua Accendio
cargo test --test full_song_lua -- --list

Fixtures are consolidated under tests/fixtures/full_song_lua and compressed at zstd level 22. See its fixture guide for selection, corpus runs, and reference maintenance.

Getting Started

Follow these steps to get the game running:

  1. Clone the Repository:

    git clone https://github.com/pnn64/deadsync.git
    cd deadsync
  2. Add Songs: Place your song packs in one of DeadSync's song scan roots:

    • the songs/ folder inside the data directory (see Data Directories)
    • the songs/ folder next to the executable
    • any folder listed in AdditionalSongFolders, AdditionalSongFoldersReadOnly, or AdditionalSongFoldersWritable

    Example structure inside a song root: <song-root>/MyPack/MySong/MySong.ssc

  3. Build the Project: Compile the game in release mode for optimal performance:

    cargo build --release
  4. Run the Game: After a successful build, run the executable from the project root:

    • On Windows:
      .\target\release\deadsync.exe
    • On Linux:
      ./target/release/deadsync
    • On macOS: Before the first run, grant Input Monitoring permissions to Terminal.app in System Settings > Privacy & Security > Input Monitoring. Without this, deadsync will not receive any keystrokes. Then, run:
      ./target/release/deadsync

Updating without starting the game

Run this from your DeadSync installation directory:

.\deadsync.exe --update

On Linux, FreeBSD, and macOS, use ./deadsync --update.

The command checks for the latest release, downloads and verifies the matching build, installs it, and exits. It prints progress without opening a game window, loading songs, or starting audio, and leaves the game closed after the update. If your version is already current or newer, it exits without downloading.

Exit status is 0 on success (including already up to date), or 1 on failure. In PowerShell scripts, use Start-Process .\deadsync.exe -ArgumentList '--update' -NoNewWindow -Wait -PassThru and read the returned process's ExitCode. --update takes precedence over --no-update-check. Installations with UpdaterInstallEnabled=0 must use their package manager or distribution's updater.

Configuration

After running the game for the first time, configuration files and a save directory will be generated.

Game Settings

You can edit deadsync.ini to change various settings, including renderer, video resolution, VSync, GfxDebug (backend validation/debugging), and the default theme color.

Input bindings

You can fully customize keyboard and gamepad controls in the [Keymaps] section of deadsync.ini. DeadSync maps virtual actions (e.g. P1_Up, P1_Start, P1_Back) to one or more physical inputs.

Keyboard

Use KeyCode::<Name> values, for example:

  • KeyCode::ArrowLeft, KeyCode::ArrowRight
  • KeyCode::KeyA, KeyCode::KeyS, KeyCode::KeyD, KeyCode::KeyW

Example:

[Keymaps]
P1_Up=KeyCode::ArrowUp,KeyCode::KeyW
P1_Start=KeyCode::Enter
P1_Back=KeyCode::Escape

Gamepad / Pad (low-level codes)

Gamepad/pad bindings are based on DeadSync’s PadCode[...] values emitted by the native input backend. The recommended way to bind a button is:

  • PadCode[0xDEADBEEF] — bind to any gamepad button with that raw code.
  • PadCode[0xDEADBEEF]@0 — bind to that code, but only on gamepad ID 0 (as shown in logs / sandbox).

To discover the codes for your device:

  1. Start the game and go to the Sandbox screen by pressing F4.
  2. Press buttons on your controller; you will see lines like:
    • Gamepad 0 [uuid=...]: RAW BTN { PadCode[0x00030030], ... }
  3. Copy the PadCode[...] part (and optionally the @0 device index) into deadsync.ini.

Example: bind P1 Start/Back to a specific button on gamepad 0:

[Keymaps]
P1_Start=PadCode[0x00030030]@0
P1_Back=PadCode[0x00030031]@0

Legacy high-level bindings like PadDir::Up, PadButton::Confirm, and PadN::Dir::Left are still accepted for convenience, but low-level PadCode[...] bindings are the most accurate and device-agnostic way to configure controllers.

Debug shortcuts

Key Action
Tab (hold) Fast-forward menus and transitions at 4×. Disabled in gameplay.
` (hold) Slow menus and transitions to 0.25×. Disabled in gameplay.
Tab + ` Halt menu animations. Disabled in gameplay.
F3 Cycle the stats overlay (off → FPS → +stutter → +timing).
Ctrl+F3 Toggle the live frame-statistics overlay: rolling per-phase frame-time graph (with idle/await-GPU segments and catch-up/spike markers), a frame-interval jitter histogram, and display-clock + audio sync-health readouts. Opens in the top-right corner, or the last position you moved it to.
Ctrl+Shift+F3 Move the frame-statistics overlay to the next corner. The chosen corner is remembered across toggles and restarts.
Ctrl+Alt+F3 Switch the frame-statistics overlay between the detailed presentation (stable decaying-histogram p99 + jitter histogram) and a minimal presentation (the graph is the jitter display — no histogram, no percentiles). Remembered across restarts.

These shortcuts mirror ITGmania's debug-loop modifiers and never affect timing-sensitive gameplay (note timing, scoring, music sync). Set TabAcceleration=0 in deadsync.ini to disable them entirely.

See Frame-Statistics Overlay for how to read the colored graph, the marker and reference lines, and the readout cells.

Profile & Online Features

A save directory is created inside the data directory to store your personal data (see Data Directories for its location).

  • To enable online features with GrooveStats, edit <data dir>/save/profiles/00000000/groovestats.ini and add your API key and username. This allows the game to fetch your online scores. The SRPG Shop also requires Password=<your GrooveStats password> in that file.
  • You can also change your in-game display name in <data dir>/save/profiles/00000000/profile.ini.

Lua chart score submission

Lua charts can submit scores when all declared song Lua compiles without errors for the current play, alongside the existing chart allowlist. Missing layers, failed compilation, partial captures, or Lua errors during play do not grant automatic approval. This result is never saved against the chart hash, since the Lua files can change independently of the notes. Other submission rules (such as autoplay, disqualifying modifiers, and course eligibility) still apply.

Golf extras

Golf features are optional and disabled by default. First enable Options > Online Score Services > ArrowCloud Options > Enable Golf Features. This master switch reveals the golf options below. Turning it off hides them and disables golf scoring, displays, and online boards while preserving saved player and Select Music choices.

  • Player Options > Advanced Modifiers > Gameplay Extras > Golf Score shows the native stroke counter. It is on by default, so golf packs score as golf as soon as the master switch is enabled. Its Golf Display setting selects golf charts only (charts with an authored par, the default) or all charts. Stroke Feedback enables ACE and penalty flashes. Each player saves these choices independently.
  • Player Options > Advanced Modifiers > Results Extras > Golf Results adds a golf evaluation pane, independently of the gameplay counter. It is also on by default and follows the same Golf Display choice.
  • Options > Select Music Options > Show Golf Pars enables wheel par badges. Turning the master switch on also turns this on, so pars appear right away; switch it off here if you would rather not see them.
  • Options > Select Music Options > GS Box Leaderboards > Golf (ArrowCloud) shows the online event board alongside the other selectable leaderboards, framed in fairway green over the In The Golf course pattern. ArrowCloud sends it with the chart's other boards in the same request, so turning it on or off takes effect immediately.
  • Options > Select Music Options > Lead With Event Boards (on by default) lists event boards such as Golf, ITL and SRPG ahead of ITG/EX on charts that are in the event, so the scorebox and leaderboard overlay open on the event before cycling to the other boards. Boards keep the order their service returned, so the service decides between events when a chart is in several.

The native display uses the supplied GolfScoring.lua curved formula: errors below 5 ms are free, hits are rounded to whole raw strokes individually, and misses, dropped holds/rolls, and hit mines cost 100 raw strokes each. One displayed stroke is 1,000 raw strokes; lower is better. Normal ITG/EX/HardEX scoring and ArrowCloud submissions continue alongside it. The online golf board shows ArrowCloud's server-calculated event scores, whose formula may differ from the native display.

Pack authors can place Golf.ini beside the pack's song folders:

[Song Folder]
Par=2.1

[Song Folder:Challenge]
Par=4

Section names use the actual song folder name; a difficulty-specific entry takes precedence over the song-wide entry. Pars are already in displayed strokes. Golf.ini never enables a feature by itself: the master switch does, after which every chart with an authored par scores as golf. Metadata refreshes when the library reloads and when entering Select Music with golf extras enabled.

Custom sound packs

DeadSync supports zmod-style "drop in a folder, play a random one" custom sounds. The bundled assets/sounds/ directory ships several folders that you can fill with your own .ogg files; the data-directory overlay ({data dir}/assets/sounds/...) is honored as well, so you don't have to modify the install. Files whose name starts with _ are ignored.

Folder Plays when
assets/sounds/evaluation_pass/ Any joined player clears (passes) on the Evaluation screen
assets/sounds/evaluation_fail/ All joined players failed on the Evaluation screen
assets/sounds/evaluation_pb/ GrooveStats submit response reports a Personal Best
assets/sounds/evaluation_wr/ GrooveStats submit response reports a World Record (rank 1)
assets/sounds/song_start/ Gameplay starts (first try; not on restart)
assets/sounds/song_start/restart/ Gameplay restarts. Naming is {n}.ogg (1.ogg, 2.ogg, ...). Falls back to restart.ogg for any restart count without a matching file

{n} in song_start/restart/ is the restart count since the last fresh entry into Gameplay; it resets to zero on SelectMusic → Gameplay and between course songs.

Custom per-style menu music works the same way: drop .ogg files into assets/music/menu/{style}/ (one of hearts, arrows, bears, ducks, cats, spooky, gay, stars, thonk, technique, srpg9) and the menu music for that visual style will randomly pick from your files. If the folder is empty the bundled per-style track plays.

The whole feature is gated by CustomSoundsEnabled in deadsync.ini (default 1). Set it to 0 to disable all folder-based sound triggers.

Direct Pad Support

DeadSync can communicate directly with dance pad hardware over USB, bypassing the OS gamepad layer. This gives you named panel inputs, stable per-pad player assignment, and the ability to read and write pad sensor configuration from within the game.

Pad Status Docs
StepManiaX ✅ Supported Documentation
FSRio ✅ Supported
Teejusb FSR firmware Serial threshold configuration; hardware validation pending Documentation
RE:Flex V2 Native input, per-sensor calibration/thresholds, and lights; hardware validation pending Documentation

Data Directories

By default, DeadSync stores user data outside the install directory so that upgrading the game doesn't risk overwriting your config, saves, or scores. Each platform uses native, XDG-compliant locations.

Default locations

Platform Data directory Cache directory
Linux / FreeBSD ~/.local/share/deadsync ($XDG_DATA_HOME/deadsync) ~/.cache/deadsync ($XDG_CACHE_HOME/deadsync)
Windows %APPDATA%\deadsync %APPDATA%\deadsync\cache
macOS ~/Library/Application Support/deadsync ~/Library/Caches/deadsync

Data directory contains user data that should be backed up:

deadsync.ini          # game configuration
deadsync.log          # current run's log (previous runs kept as deadsync-<timestamp>.log)
save/
  profiles/           # player profiles, scores, settings
  screenshots/        # captured screenshots
songs/                # default song scan root
courses/              # course files

Cache directory contains regenerable data that can be freely deleted:

songs/                # parsed song metadata cache
banner/               # banner image cache
cdtitle/              # CD title image cache
downloads/            # temporary download data
noteskins/            # compiled noteskin cache
unlocks-cache.json    # online unlock cache

Portable mode

If you prefer a fully self-contained install (e.g. for arcade cabs or USB sticks), create an empty file named portable.txt next to the executable. When this file is present, DeadSync stores everything in the executable's directory — the same behavior as older versions.

The file's contents are ignored; only its presence matters.

Song directories

DeadSync scans for songs in the following locations:

  1. The songs/ folder inside the data directory.
  2. The songs/ folder next to the executable (in non-portable mode, so bundled songs are always found).
  3. Any additional folders listed in AdditionalSongFolders, AdditionalSongFoldersReadOnly, or AdditionalSongFoldersWritable in deadsync.ini.

Course files follow the same pattern: DeadSync scans the data-directory courses/ root first and, in non-portable mode, also scans the courses/ folder next to the executable.

Migration

On first run in non-portable mode, if DeadSync finds a deadsync.ini next to the executable but not in the data directory, it will automatically copy deadsync.ini, save/, and legacy cache subdirectories into the new data/cache locations. Install-folder songs/ and courses/ are not copied; they remain in place and are still scanned in non-portable mode. The originals are not deleted — you can clean them up manually after verifying everything works.

Contributing

We welcome contributions of all sizes. Keep changes focused, and open a discussion or draft PR when the direction is uncertain.

  • Keep layer boundaries sharp: engine is reusable infrastructure, game owns domain rules, screens owns UI, and app/assets bridge systems.
  • Favor idiomatic, direct Rust: small cohesive functions, short names, explicit ownership, and methods or traits only when they reduce complexity.
  • Use Result for external failures, panic only on programmer errors, document unsafe, and test observable behavior rather than implementation details.
  • Profile before optimizing hot paths. Pre-size and reuse buffers, favor contiguous data, and avoid surprise allocations, I/O, or locks during gameplay.
  • Remove dead or replaced code and keep dependencies lean. Run cargo fmt --check, cargo check, cargo clippy, and relevant tests before submitting.

Collecting Logs

When reporting a bug, attaching a log file helps the team diagnose the problem quickly.

1. Enable file logging

Open deadsync.ini (created after the first run; see Data Directories) and set the following under [Options]:

[Options]
LogToFile=1
LogLevel=Debug
Setting Values Default
LogToFile 1 (enabled) / 0 (disabled) 1
LogLevel Error, Warn, Info, Debug, Trace Warn

Use Debug for most bug reports. Use Trace only if asked—it produces significantly more output.

2. Reproduce the issue

Launch the game and reproduce the problem. The log is written to deadsync.log in the data directory (see Data Directories above). The previous two runs are kept alongside it as deadsync-<timestamp>.log.

3. Share the log

Attach deadsync.log to your GitHub issue or discussion. If the file is large, compress it first (.zip / .gz).

Privacy note: The log does not contain passwords or API keys, but it does include file paths and song/pack names from your system. Review the file before sharing if that is a concern.

Acknowledgements

DeadSync would not exist without years of work from the StepMania and ITG communities. In particular, we would like to acknowledge:

And more broadly, everyone who has written themes, noteskins, tools, and simfiles for the community over the years. ❤️

About

DeadSync is an ITG/StepMania engine with Vulkan/OpenGL backends, focused on perfect sync and competitive-level performance.

Resources

Stars

54 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages