Skip to content

Repository files navigation

Open Rhythm

White cubes fly at a 3×3 grid. Your cursor has to be on the cube when it lands. Where inside the cell you caught it decides the rank — timing barely matters, aim does.

build release licence godot

Download · Telegram · Discord

Open Rhythm is a game, not a music player. The tracks are here so there is something to play to. If a song is good, go listen to it where the artist actually gets paid.

Gameplay
Map editor
Map editor — piano roll, waveform, beat grid, undo, hold dragging
Main menu
Main menu — carousel, lifetime score, update banner
Versus
Versus — two players, one chart, no server in between
Stats
Stats — records, achievements, saved replays

Getting it

Grab a build for your platform from Releases. The game checks for a newer release when the menu opens and offers to update itself — one click, it downloads, installs and restarts. That check can be turned off in Settings → Video.

Or run it from source with Godot 4.7:

godot --path .
What's in it
Square mode 3×3 grid, zone-based judging, PERFECT → BULLSHIT ranks
Hold notes long cubes you have to carry to the end — nothing else is charted while one runs, because there is only one cursor
Click notes cubes that have to be pressed, not just covered. Off by default; the CLICKS modifier turns on the ones a mapper put in
Modifiers TARGET, BLACKOUT, CAGE, SPEED UP, SLOW DOWN, MIRROR, HIDDEN, CLICKS, CLICKY — each with its own score multiplier
Three stories learn with Melly, then Night Drive, then Overdrive
Six OST tracks written by tools/gen_media.py and tools/gen_pack2.py, with Easy / Normal / Hyper charts
Map editor piano roll with a waveform and beat grid, snapping, undo, copy/paste, multi-select, hold dragging, and an onset-based auto-generator
Map scripting a song can change the background colour, throw its own pictures behind the playfield, reskin the cubes and shake the camera
Imports .sspm (Rhythia / Sound Space Plus, v1 and v2) and the legacy Sound Space map string
Versus two players, one chart, higher score wins — a direct connection, no server
Offset calibration tap along with a metronome and the game works out your audio delay
Replays plus records, lifetime stats and 23 achievements
Accessibility reduce motion, reduce flashes, cursor size, video off
Six languages English, Russian, Chinese, German, Dutch, Spanish
Controls mouse, touch, gamepad stick or keyboard, with rebindable keys
VR the desktop build opens in a headset when one is running and in a window when it is not; Quest and Pico get their own packages. Aim with a laser pointer, or hold a saber in each hand and cut the cubes from any direction

Runs on Linux, Windows, Android and the web.

Adding your own songs

Drop a folder or a .zip into the library folder shown on the Songs screen, then press RESCAN. A song folder is just this:

my_song/
├── map.json        required
├── audio.ogg       .wav, .ogg or .mp3
├── video.ogv       optional, played behind the playfield
└── events.json     optional, see map scripting

The Songs screen also imports .sspm maps from Rhythia / Sound Space directly — audio and cover art come along with them.

Full format reference: docs/MAP_FORMAT.md

Making maps

The 3×3 playfield on the left places notes at the playhead; the timeline at the bottom is where you actually shape the chart.

Space play / pause
← → seek by the snap step (Shift: a bar)
1…8 snap 1/1 … 1/16
Ctrl+Z / Ctrl+Y undo / redo
Ctrl+C / Ctrl+V copy / paste at the playhead
Ctrl+D duplicate the selection forward
Ctrl+A / Delete select all / delete the selection
H toggle a one-beat hold on the selection
C toggle click notes on the selection
Ctrl+S / T save / test the chart
drag rubber-band select
Ctrl+click add a note
drag right edge set a hold length
wheel / Ctrl+wheel / middle drag scroll / zoom / pan

Saving a song that ships with the game forks it into your library first — it asks what to call the copy. res:// lives inside the binary and cannot be written to.

The waveform is drawn for .wav audio, or from a waveform.json if the song ships one.

Versus

One player hosts, the other types their address. The connection is direct — nothing goes through a server and nothing is uploaded anywhere, so both machines have to be able to reach each other: the same network, or the host forwarding port 27015.

Both sides must be on the same build and holding the same chart. The handshake compares a protocol number, the game version and a SHA-256 of the notes themselves, so custom maps work exactly like the bundled ones as long as the charts are identical. The audio file name and any video are left out of that hash — neither changes what you play.

VR

Headsets are not there yet. The desktop side is built and the room draws, but the Quest and Pico packages crash on startup inside the engine's own OpenXR - on a Pico 4 they do not survive half a second. What has been ruled out, and what it needs next, is written down in docs/VR_STATUS.md. The desktop build's VR path has never been run against a real runtime either, so treat it as untested.

You can look at the whole thing without a headset: Settings → VR → Look at the VR room on this screen.

There is no VR build of the desktop game and no switch to flip. It looks for an OpenXR runtime at startup: SteamVR running and a headset awake means it opens in VR, nothing there means it opens in a window. Settings → VR can say no, and picks how you hit the cubes.

Laser pointer puts the grid across the room and aims a beam at it; the trigger is a click, which is what click notes want. Sabers bring the grid to arm's length and put a blade in each hand. A cube counts the moment a blade sweeps through it, from any direction - the cubes have no side to cut them on, so swing the way that feels right. Grip + B recentres the room, and the menu button steps back the way Esc does.

Everything else is the flat game: the same charts, the same judging, the same menus and editor, running on a screen hanging in front of you. Only the cubes come out of it into the room.

Quest and Pico get their own packages. Building them needs the OpenXR loaders, which are not in this repository:

curl -LO https://github.com/GodotVR/godot_openxr_vendors/releases/download/5.1.0-stable/godotopenxrvendorsaddon.zip
unzip -q godotopenxrvendorsaddon.zip -d /tmp/vendors
cp -r /tmp/vendors/asset/addons/godotopenxrvendors addons/

godot --headless --path . --install-android-build-template --export-release "Android" build/OpenRhythm.apk
tools/build_headsets.sh

Those two are gradle builds, so they need the Android SDK and a JDK the same way the phone package does. They also need the Mobile renderer: OpenXR on Android binds to the graphics API when it creates its session and wants Vulkan, and on the OpenGL ES binding the interface loads while the session never does - which from inside the headset looks like the app hanging on its loading screen. The engine picks a renderer before it knows an export's custom features, so that cannot be a per-preset setting; the script flips it for the length of those two builds and puts it back afterwards.

Building

Godot 4.7 with the matching export templates installed.

godot --headless --path . --export-release "Linux" build/linux/OpenRhythm.x86_64
godot --headless --path . --export-release "Windows Desktop" build/windows/OpenRhythm.exe
godot --headless --path . --export-release "Web" build/web/index.html

The web export needs cross-origin isolation headers (COOP/COEP) on the host; itch.io has a checkbox for it. Theora video is excluded from the web build, so songs with a clip play without one.

Android signing. Release credentials are never stored in export_presets.cfg — CI fails the build if they end up there. Pass them through the environment instead:

export GODOT_ANDROID_KEYSTORE_RELEASE_PATH=/path/to/release.keystore
export GODOT_ANDROID_KEYSTORE_RELEASE_USER=your-alias
export GODOT_ANDROID_KEYSTORE_RELEASE_PASSWORD=...
godot --headless --path . --export-release "Android" build/android/OpenRhythm.apk

CI builds the APK too, once the same key is stored as repository secrets (ANDROID_KEYSTORE_BASE64, ANDROID_KEYSTORE_USER, ANDROID_KEYSTORE_PASSWORD); without them that job simply skips.

Releases are cut by pushing a tag — CI builds every platform and attaches the archives:

git tag v0.3.1 && git push origin v0.3.1
Music and rights

The bundled OST — Neon Drift, Hyper Drive, Midnight Pulse, Bass Rush, Crimson Step, Afterburner and the tutorial track — is generated by the scripts in tools/ and ships under the project licence.

Music by anyone else is not built into the game and is not in this repository. It travels as an installable song zip instead - drop the file in the songs folder and the game unpacks it on the next scan:

python3 tools/pack_song.py local_songs/my_song

Keep those folders in local_songs/ with an empty .gdignore beside them, or the engine will import and pack them anyway - that one file is the difference between a 37 MB download and a 96 MB one.

That keeps someone else's recording out of the build, and out of every clone of this repo, while still being one file to hand a friend. The game shows a notice on first launch pointing players at the original releases.

If you are an artist and want a track out of the game, say so in Telegram or Discord and it goes.

Contributing

The most useful thing you can add is a map. Build one in the editor and open a pull request with the song folder — without the audio unless you own it.

Code is plain Godot 4 + GDScript, no plugins. There is a headless test harness:

godot --headless --path . res://tools/CoreTest.tscn     # saves, judging, charts
godot --headless --path . res://tools/LayoutTest.tscn   # every screen, six aspect ratios

See CONTRIBUTING.md for house style, the translation workflow and the chart generators.


Code under MIT · fonts under SIL OFL 1.1 · music per track

Made by narezany

About

Cursor-based rhythm game in Godot 4. Holds, click notes, a map editor, peer-to-peer versus, and it imports Rhythia / Sound Space maps.

Topics

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages