Skip to main content

Overview

The notes system handles note rendering, hit detection, and strumline management. The Strumline class manages receptors, notes, and sustains for a single player, while NoteSprite represents individual notes.

Class Hierarchy

FlxSpriteGroup
  └── Strumline
        ├── notes (FlxTypedSpriteGroup<NoteSprite>)
        ├── holdNotes (FlxTypedSpriteGroup<SustainTrail>)
        ├── strumlineNotes (FlxTypedSpriteGroup<StrumlineNote>)
        └── noteSplashes (FlxTypedSpriteGroup<NoteSplash>)

FunkinSprite
  └── NoteSprite

Strumline

A group of sprites that handles receptors, notes, and note splashes for a given player.

Constants

Strumline.DIRECTIONS = [LEFT, DOWN, UP, RIGHT];
Strumline.STRUMLINE_SIZE = 104; // pixels
Strumline.NOTE_SPACING = 112; // pixels (104 + 8)
Strumline.KEY_COUNT = 4;

Properties

isPlayer
Bool
Whether this strumline is controlled by player input (true) or CPU (false)
scrollSpeed
Float
default:"1.0"
Scroll speed multiplier for notes
isDownscroll
Bool
Whether notes scroll downward instead of upward
notes
FlxTypedSpriteGroup<NoteSprite>
Active notes heading toward the strumline
holdNotes
FlxTypedSpriteGroup<SustainTrail>
Active hold/sustain notes
strumlineNotes
FlxTypedSpriteGroup<StrumlineNote>
The receptor arrows (4 directions)
noteData
Array<SongNoteData>
Note data from the chart
showNotesplash
Bool
default:"true"
Whether to show note splash effects on sick hits

Methods

new(noteStyle:NoteStyle, isPlayer:Bool, ?scrollSpeed:Float)

Creates a new strumline.
var playerStrumline = new Strumline(noteStyle, true, 1.5);
var opponentStrumline = new Strumline(noteStyle, false, 1.5);
noteStyle
NoteStyle
required
Note style for visuals (e.g., “funkin”, “pixel”)
isPlayer
Bool
required
True for player-controlled, false for CPU
scrollSpeed
Float
Initial scroll speed multiplier

applyNoteData(data:Array<SongNoteData>):Void

Loads note data from a chart.
// Load notes for this strumline
var playerNotes = chartData.notes.filter(n -> n.getMustHitNote());
strumline.applyNoteData(playerNotes);
data
Array<SongNoteData>
required
Array of note data from the chart

addNoteData(note:SongNoteData, sort:Bool = true):Void

Adds a single note to the strumline.
var newNote:SongNoteData = {
  time: 1000.0,
  data: 2, // UP
  length: 0.0
};
strumline.addNoteData(newNote);

pressKey(dir:NoteDirection, keyCode:Int):Void

Registers a key press.
// Called by input system
strumline.pressKey(NoteDirection.LEFT, FlxKey.A);

releaseKey(dir:NoteDirection, ?keyCode:Int):Void

Registers a key release.
strumline.releaseKey(NoteDirection.LEFT, FlxKey.A);

// Release all keys for this direction
strumline.releaseKey(NoteDirection.LEFT);

isKeyHeld(dir:NoteDirection):Bool

Checks if a direction is being held.
if (strumline.isKeyHeld(NoteDirection.DOWN)) {
  trace('Down is held!');
}

hitNote(note:NoteSprite, removeNote:Bool = true):Void

Marks a note as hit.
strumline.hitNote(note, true); // Hit and remove
strumline.hitNote(note, false); // Hit but keep visible
note
NoteSprite
required
The note that was hit
removeNote
Bool
default:"true"
If true, removes note immediately. If false, makes it transparent.

killNote(note:NoteSprite):Void

Kills a note (for misses or cleanup).
strumline.killNote(note);

playConfirm(direction:NoteDirection):Void

Plays the confirm animation on a receptor.
strumline.playConfirm(NoteDirection.LEFT);

playPress(direction:NoteDirection):Void

Plays the press animation on a receptor.
strumline.playPress(NoteDirection.UP);

playStatic(direction:NoteDirection):Void

Returns a receptor to its static/idle state.
strumline.playStatic(NoteDirection.RIGHT);

playNoteSplash(direction:NoteDirection):Void

Plays a note splash effect.
// On sick rating
strumline.playNoteSplash(NoteDirection.DOWN);

getNotesMayHit():Array<NoteSprite>

Returns notes within the hit window.
var hittableNotes = strumline.getNotesMayHit();
for (note in hittableNotes) {
  // Check for inputs
}

clean():Void

Resets the strumline, removing all notes and resetting state.
// When restarting
strumline.clean();

vwooshNotes():Void

Animates all notes flying off-screen (for restarts).
strumline.vwooshNotes();

vwooshInNotes():Void

Animates notes flying in from off-screen.
strumline.vwooshInNotes();

resetScrollSpeed(?newScrollSpeed:Float):Void

Resets scroll speed to chart default or specified value.
strumline.resetScrollSpeed(); // Use chart speed
strumline.resetScrollSpeed(2.0); // Set to 2.0x

NoteSprite

Represents a single note heading toward the strumline.

Properties

strumTime
Float
Time when the note should be hit (milliseconds)
length
Float
default:"0.0"
Hold note length in milliseconds (0 for single notes)
direction
NoteDirection
Direction of the note (LEFT, DOWN, UP, RIGHT)
kind
Null<String>
Note kind (e.g., “alt”, “hurt”) for special behavior
noteData
SongNoteData
Reference to the chart data for this note
hasBeenHit
Bool
default:"false"
True if the note has been hit
hasMissed
Bool
default:"false"
True if the note was missed
mayHit
Bool
default:"false"
True if the note is within the hit window
tooEarly
Bool
default:"false"
True if the note is before the hit window
scoreable
Bool
default:"true"
Whether this note contributes to score/accuracy
isHoldNote
Bool
True if this is a hold note (length > 0)
holdNoteSprite
SustainTrail
Reference to the hold note sprite

Methods

new(noteStyle:NoteStyle, direction:Int = 0)

Creates a new note sprite.
var note = new NoteSprite(noteStyle, NoteDirection.LEFT);

setupNoteGraphic(noteStyle:NoteStyle):Void

Sets up the note’s visual appearance.
note.setupNoteGraphic(pixelNoteStyle);

getParam(name:String):Null<Dynamic>

Retrieves a custom parameter value.
var customValue = note.getParam('scrollMult');
if (customValue != null) {
  scrollSpeed *= customValue;
}

desaturate():Void

Desaturates the note (used after hitting).
note.desaturate(); // Make it grayscale

setHue(hue:Float):Void

Changes the note’s hue.
note.setHue(180.0); // Shift color

Hit Detection

Hit Window

Notes can be hit within a time window defined by Constants.HIT_WINDOW_MS:
var hitWindow = Constants.HIT_WINDOW_MS; // e.g., 166.67ms

if (Math.abs(note.strumTime - Conductor.songPosition) <= hitWindow) {
  // Note is hittable
}

Judgement Timing

Typical timing windows:
  • Sick: ±45ms
  • Good: ±90ms
  • Bad: ±135ms
  • Shit: ±166ms

Hold Notes (SustainTrail)

Hold notes are rendered as trails that follow regular notes:

Properties

  • sustainLength: Remaining length in milliseconds
  • fullSustainLength: Original total length
  • hitNote: Whether the note head was hit
  • missedNote: Whether the hold was dropped
  • cover: Visual cover that appears when holding

Behavior

  1. Note head must be hit first
  2. Hold note clips as it’s held
  3. Dropped if key is released early
  4. Completed when sustainLength reaches 0

Usage Examples

Creating a Strumline

// Create strumlines for both players
var noteStyle = NoteStyleRegistry.instance.fetchEntry('funkin');

var playerStrumline = new Strumline(noteStyle, true, 1.0);
playerStrumline.x = 730;
playerStrumline.y = 100;

var opponentStrumline = new Strumline(noteStyle, false, 1.0);
opponentStrumline.x = 100;
opponentStrumline.y = 100;

// Load note data
var chart = SongRegistry.instance.parseEntryChartData(songId, difficulty);
var playerNotes = chart.notes.filter(n -> n.getMustHitNote());
var opponentNotes = chart.notes.filter(n -> !n.getMustHitNote());

playerStrumline.applyNoteData(playerNotes);
opponentStrumline.applyNoteData(opponentNotes);

Handling Input

// On key press
if (controls.NOTE_LEFT_P) {
  playerStrumline.pressKey(NoteDirection.LEFT, FlxKey.A);
  
  var hittableNotes = playerStrumline.getNotesMayHit()
    .filter(n -> n.direction == NoteDirection.LEFT);
  
  if (hittableNotes.length > 0) {
    var note = hittableNotes[0];
    var diff = Math.abs(note.strumTime - Conductor.songPosition);
    
    if (diff <= 45) {
      // Sick hit!
      playerStrumline.hitNote(note);
      playerStrumline.playNoteSplash(NoteDirection.LEFT);
    }
  }
}

// On key release
if (controls.NOTE_LEFT_R) {
  playerStrumline.releaseKey(NoteDirection.LEFT, FlxKey.A);
}

Custom Note Behavior

// Create a note with custom parameters
var bombNote:SongNoteData = {
  time: 2000.0,
  data: 1, // DOWN
  length: 0.0,
  kind: 'hurt',
  params: [
    { name: 'damage', value: 0.5 }
  ]
};

strumline.addNoteData(bombNote);

// Later, check note kind
if (note.kind == 'hurt') {
  var damage = note.getParam('damage') ?? 0.1;
  health -= damage;
}

Best Practices

Always check note.mayHit before allowing hits. Notes outside the hit window should not be hittable.
Use getNotesMayHit() to get only notes that can currently be hit, avoiding unnecessary iteration.
The strumline automatically recycles note sprites for performance. Don’t destroy notes manually unless absolutely necessary.

Build docs developers (and LLMs) love