Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/FunkinCrew/Funkin/llms.txt

Use this file to discover all available pages before exploring further.

The charting system defines note placement, events, and gameplay data for songs in Friday Night Funkin’.

Chart Data Format

Chart data is stored in assets/data/songs/[id]/[id]-chart.json:
{
  "version": "2.2.4",
  "scrollSpeed": {
    "easy": 1.0,
    "normal": 1.5,
    "hard": 2.0
  },
  "events": [
    {
      "t": 0,
      "e": "FocusCamera",
      "v": {"char": 1}
    },
    {
      "t": 4000,
      "e": "FocusCamera",
      "v": {"char": 0}
    }
  ],
  "notes": {
    "easy": [
      {"t": 1000, "d": 0, "l": 0},
      {"t": 1500, "d": 1, "l": 0},
      {"t": 2000, "d": 2, "l": 0},
      {"t": 2500, "d": 3, "l": 0}
    ],
    "normal": [
      {"t": 1000, "d": 0, "l": 250},
      {"t": 1500, "d": 1, "l": 0},
      {"t": 2000, "d": 2, "l": 500},
      {"t": 2500, "d": 3, "l": 0}
    ],
    "hard": []
  },
  "generatedBy": "FunkinCrew Chart Editor"
}

Chart Data Fields

version
String
required
Chart format version (currently “2.2.4”)
scrollSpeed
Map<String, Float>
required
Scroll speed for each difficulty (controls note speed)
events
Array<SongEventData>
required
Array of song events (camera focus, animations, etc.)
notes
Map<String, Array<SongNoteData>>
required
Note data for each difficulty
generatedBy
String
Tool or person that generated this chart

Song Note Data

Each note is defined with minimal data:
class SongNoteData
{
  public var t:Float;    // Time (in song's time format)
  public var d:Int;      // Direction (0-7)
  public var l:Float;    // Length (0 = regular note, >0 = hold note)
  public var k:String;   // Kind (optional, for special note types)
}

Note Fields

t
Float
required
Time when the note should be hit (format depends on song’s timeFormat)
d
Int
required
Note direction/lane (0-3 for opponent, 4-7 for player)
  • 0/4 = Left
  • 1/5 = Down
  • 2/6 = Up
  • 3/7 = Right
l
Float
default:"0"
Hold note length in milliseconds (0 = regular note, >0 = sustain)
k
String
Note kind ID for special note types (e.g., “mine”, “hurt”)

Note Direction Mapping

Opponent strumline (left side):
  • 0 = Left
  • 1 = Down
  • 2 = Up
  • 3 = Right
Player strumline (right side):
  • 4 = Left
  • 5 = Down
  • 6 = Up
  • 7 = Right
enum abstract NoteDirection(Int)
{
  var LEFT = 0;
  var DOWN = 1;
  var UP = 2;
  var RIGHT = 3;
}

// Convert to player lane
var playerLane = baseDirection + 4;

Note Types

Regular Notes

Standard notes that must be hit:
{"t": 1000, "d": 0, "l": 0}

Hold Notes (Sustains)

Notes that must be held down:
{"t": 1000, "d": 0, "l": 500}
Hold for 500ms after the note starts. The l field defines the hold duration. Hold note scoring:
  • Player must hit the note head
  • Player must hold the key for the sustain duration
  • Releasing early breaks the hold
  • Each sustain “segment” can award points

Special Note Kinds

Note kinds modify behavior:
{"t": 1000, "d": 0, "l": 0, "k": "mine"}
Built-in note kinds:
  • Default (no k field): Standard note
  • Custom kinds defined in assets/data/notekind/ directory
Custom note kinds can:
  • Change note appearance
  • Modify hit behavior
  • Award different points
  • Trigger special effects

Song Events

Events trigger gameplay actions at specific times:
class SongEventData
{
  public var t:Float;         // Time
  public var e:String;        // Event kind
  public var v:Dynamic;       // Event value (data)
  public var activated:Bool;  // Internal tracking
}

Event Structure

{
  "t": 4000,
  "e": "FocusCamera",
  "v": {
    "char": 1,
    "duration": 1.0
  }
}
t
Float
required
Time when event triggers (in song’s time format)
e
String
required
Event kind/type identifier
v
Dynamic
Event-specific data (can be any JSON-serializable value)

Built-in Events

FocusCamera

Changes camera focus to a character:
{
  "t": 1000,
  "e": "FocusCamera",
  "v": {
    "char": 1
  }
}
Parameters:
  • char: Character index (0 = player, 1 = opponent, 2 = girlfriend)
  • duration: Optional tween duration in seconds

ZoomCamera

Changes camera zoom level:
{
  "t": 2000,
  "e": "ZoomCamera",
  "v": {
    "zoom": 1.2,
    "duration": 0.5
  }
}
Parameters:
  • zoom: Target zoom level
  • duration: Tween duration in seconds

PlayAnimation

Plays animation on a character or prop:
{
  "t": 3000,
  "e": "PlayAnimation",
  "v": {
    "target": "dad",
    "anim": "hey",
    "force": true
  }
}
Parameters:
  • target: Character or prop name
  • anim: Animation name
  • force: Whether to interrupt current animation

SetCameraBop

Controls camera bopping:
{
  "t": 4000,
  "e": "SetCameraBop",
  "v": {
    "intensity": 0.015,
    "rate": 4
  }
}
Parameters:
  • intensity: Bop strength (zoom amount)
  • rate: Bop every X beats (e.g., 4 = every 4 beats)

Scroll Speed

Controls how fast notes move:
{
  "scrollSpeed": {
    "easy": 1.0,
    "normal": 1.5,
    "hard": 2.0,
    "expert": 2.5
  }
}
Scroll speed values:
  • 1.0 = Default speed
  • <1.0 = Slower (more reaction time)
  • >1.0 = Faster (less reaction time)
  • Typical range: 0.8 - 3.0
How scroll speed affects gameplay:
  • Higher values = notes approach faster
  • Lower values = more time to react
  • Does NOT affect timing windows
  • Purely visual speed change

Chart Organization

Per-Difficulty Charts

Each difficulty has separate note data:
{
  "notes": {
    "easy": [
      {"t": 1000, "d": 0, "l": 0},
      {"t": 2000, "d": 1, "l": 0}
    ],
    "normal": [
      {"t": 1000, "d": 0, "l": 0},
      {"t": 1500, "d": 2, "l": 0},
      {"t": 2000, "d": 1, "l": 0}
    ],
    "hard": [
      {"t": 1000, "d": 0, "l": 250},
      {"t": 1250, "d": 2, "l": 0},
      {"t": 1500, "d": 1, "l": 0},
      {"t": 1750, "d": 3, "l": 0},
      {"t": 2000, "d": 0, "l": 500}
    ]
  }
}
Difficulty design guidelines:
  • Easy: Fewer notes, simpler patterns, longer gaps
  • Normal: Moderate note density, basic patterns
  • Hard: Higher density, complex patterns, more sustains

Shared Events

Events are shared across all difficulties:
{
  "events": [
    {"t": 0, "e": "FocusCamera", "v": {"char": 1}},
    {"t": 4000, "e": "FocusCamera", "v": {"char": 0}}
  ]
}
Camera changes, animations, and other events apply to all difficulties.

Note Patterns

Single Notes

Basic rhythm:
[
  {"t": 1000, "d": 0, "l": 0},
  {"t": 1500, "d": 1, "l": 0},
  {"t": 2000, "d": 2, "l": 0},
  {"t": 2500, "d": 3, "l": 0}
]

Chords

Multiple simultaneous notes:
[
  {"t": 1000, "d": 0, "l": 0},
  {"t": 1000, "d": 2, "l": 0}
]
Both notes at the same time (t: 1000).

Rolls

Rapid sequential notes:
[
  {"t": 1000, "d": 0, "l": 0},
  {"t": 1125, "d": 1, "l": 0},
  {"t": 1250, "d": 2, "l": 0},
  {"t": 1375, "d": 3, "l": 0}
]

Holds (Sustains)

Long notes:
[
  {"t": 1000, "d": 0, "l": 1000},
  {"t": 2500, "d": 1, "l": 500}
]
First note holds for 1 second, second for 0.5 seconds.

Jacks

Repeated notes in same lane:
[
  {"t": 1000, "d": 0, "l": 0},
  {"t": 1250, "d": 0, "l": 0},
  {"t": 1500, "d": 0, "l": 0},
  {"t": 1750, "d": 0, "l": 0}
]

Timing Precision

Chart timing uses the song’s time format:

Milliseconds (Most Common)

{"t": 1234.5, "d": 0, "l": 0}
Direct millisecond timing. Precise to 0.1ms.

Ticks

{"t": 384, "d": 0, "l": 0}
With divisions: 96:
  • 1 beat = 96 ticks
  • 1/4 beat = 24 ticks
  • 1/16 beat = 6 ticks
Grid-aligned, no floating point errors.

Step Alignment

Notes should align to musical steps: 4/4 time, 120 BPM:
  • 1 beat = 500ms
  • 1 step (1/4 beat) = 125ms
Common note timings:
  • Quarter notes: 500ms apart
  • Eighth notes: 250ms apart
  • Sixteenth notes: 125ms apart
  • Thirty-second notes: 62.5ms apart

Loading Charts

Charts are loaded via the Song system:
var song = SongRegistry.instance.fetchEntry("bopeebo");
var difficulty = song.getDifficulty("hard");

// Access chart data
var notes = difficulty.notes;  // Array<SongNoteData>
var events = difficulty.events; // Array<SongEventData>
var scrollSpeed = difficulty.getScrollSpeed();

Charting Best Practices

1. Follow the Music

Align notes to musical beats:
  • Use strong beats for emphasis
  • Match note patterns to melody
  • Use holds for sustained notes

2. Difficulty Progression

Easy:
  • Simple patterns
  • Mostly single notes
  • Clear beat alignment
  • Minimal holds
Normal:
  • Moderate complexity
  • Some chords (2 notes)
  • Basic patterns
  • Occasional holds
Hard:
  • Complex patterns
  • Frequent chords
  • Fast rolls
  • Long holds
  • Creative patterns

3. Playtest Extensively

  • Test on different difficulties
  • Check timing accuracy
  • Verify event triggers
  • Ensure patterns are fair

4. Use Events Effectively

  • Camera focus on singing character
  • Zoom for emphasis
  • Animations at key moments
  • Don’t overuse effects

5. Balance Opponent and Player

  • Opponent notes (d: 0-3) during their sections
  • Player notes (d: 4-7) during player sections
  • Can overlap during duets

Example: Complete Chart

{
  "version": "2.2.4",
  "scrollSpeed": {
    "easy": 1.0,
    "normal": 1.5,
    "hard": 2.0
  },
  "events": [
    {
      "t": 0,
      "e": "FocusCamera",
      "v": {"char": 1}
    },
    {
      "t": 8000,
      "e": "FocusCamera",
      "v": {"char": 0}
    },
    {
      "t": 12000,
      "e": "ZoomCamera",
      "v": {"zoom": 1.3, "duration": 0.5}
    }
  ],
  "notes": {
    "normal": [
      {"t": 1000, "d": 0, "l": 0},
      {"t": 1500, "d": 1, "l": 0},
      {"t": 2000, "d": 2, "l": 0},
      {"t": 2500, "d": 3, "l": 0},
      {"t": 3000, "d": 0, "l": 500},
      
      {"t": 8000, "d": 4, "l": 0},
      {"t": 8500, "d": 5, "l": 0},
      {"t": 9000, "d": 6, "l": 0},
      {"t": 9500, "d": 7, "l": 0},
      {"t": 10000, "d": 4, "l": 0},
      {"t": 10000, "d": 6, "l": 0},
      
      {"t": 12000, "d": 4, "l": 250},
      {"t": 12250, "d": 5, "l": 0},
      {"t": 12500, "d": 6, "l": 0},
      {"t": 12750, "d": 7, "l": 0}
    ]
  },
  "generatedBy": "Chart Editor"
}
This chart demonstrates:
  • Camera focus changes at 0ms and 8000ms
  • Camera zoom at 12000ms
  • Opponent notes (d: 0-3) from 1000-3000ms
  • Player notes (d: 4-7) from 8000ms onward
  • Various patterns: singles, holds, chords, rolls

Build docs developers (and LLMs) love