| name | fxgl-rhythm |
| description | Build a rhythm game in FXGL โ parse a beatmap, spawn notes early enough to scroll into a hit zone on time, score inputs by timing window, handle combo and grades, support hold notes, and keep chart timing aligned with audio as closely as FXGL's current audio API allows.
|
| triggers | ["rhythm game","beatmap","note highway","timing window","perfect good miss","combo multiplier","hold note","osu","guitar hero"] |
| compatibility | Java 17+, FXGL 21.x. Audio playback is available, but FXGL's public AudioPlayer API does not expose authoritative music position, so chart timing must be driven by engine time or a custom integration layer.
|
| category | fxgl/game-types |
| tags | ["fxgl","java","javafx","game-types","rhythm","audio"] |
| metadata | {"author":"fxgl-skills","version":"1.0","fxgl-version":"21.1"} |
| allowed-tools | ["Read","Write","Edit","Bash"] |
FXGL Rhythm Game
Important Timing Constraint
FXGL's public AudioPlayer API does not expose a music-position query. That means the safest
default approach is:
- start music
- start a
songTime accumulator in onUpdate(double tpf)
- align notes to that accumulator
This is good enough for prototypes, but it is not sample-accurate.
Beatmap Model
public enum NoteType { TAP, HOLD }
public record Note(double time, int lane, NoteType type, double holdDuration) {}
public record Beatmap(double leadInSeconds, List<Note> notes) {}
Starting Playback
private Music song;
private Beatmap beatmap;
private boolean chartRunning = false;
private double songTime = 0.0;
private static final double SCROLL_DURATION = 1.6;
private void startSong() {
song = getAssetLoader().loadMusic("track.mp3");
getAudioPlayer().playMusic(song);
songTime = -beatmap.leadInSeconds();
chartRunning = ;
}
{
(!chartRunning)
;
songTime += tpf;
spawnUpcomingNotes();
detectMisses();
}