Sprite atlases
Sprites are packed into PNG spritesheets with Sparrow XML sidecar files (the format produced by TexturePacker). Each atlas consists of a.png image and a .xml file with the same base name, placed in the images/ directory.
XML structure
SubTexture attributes
Frame name including a zero-padded index suffix (e.g.
BF idle dance0000). The engine strips trailing digits to derive the animation prefix (BF idle dance).X position of the frame within the spritesheet, in pixels.
Y position of the frame within the spritesheet, in pixels.
Width of the source frame region, in pixels.
Height of the source frame region, in pixels.
Horizontal offset applied when placing the trimmed frame back into the original canvas. Typically negative, representing trimmed left margin.
Vertical offset applied when placing the trimmed frame back into the original canvas. Typically negative, representing trimmed top margin.
Width of the original (untrimmed) canvas for this frame. Omit if no trimming was applied.
Height of the original (untrimmed) canvas for this frame. Omit if no trimming was applied.
Whether the frame was rotated 90 degrees clockwise in the sheet.
Animation name derivation
The engine groups frames into animations by stripping trailing digits from eachSubTexture name:
BF idle dance0000→ animation prefixBF idle danceBF NOTE LEFT0000→ animation prefixBF NOTE LEFT
Charts
Charts use the Psych Engine JSON format. The file is wrapped in a top-level"song" object.
Outer wrapper
{ "song": { "song": { ... } } } produced by some older editors.
Song fields
Song name used for display and asset lookup.
Starting BPM. Individual sections can override this with
changeBPM + bpm.Note scroll speed multiplier.
Global audio offset in milliseconds applied to all note timing.
Character name for the player (boyfriend) slot.
Character name for the opponent slot.
Character name for the girlfriend slot.
Stage name. The engine loads
stages/{stage}.json.Whether the song has a separate vocals track (
Voices.ogg).Format identifier. When set to
psych_v1 or psych_v1_convert, note directions are treated as absolute (0–3 = player, 4–7 = opponent) and the legacy direction-flip conversion is skipped.Array of chart sections. See section format below.
Top-level events array. See events format below.
Custom note skin path override for both sides.
Custom note skin override for the opponent side (VS Retrospecter extension).
Custom note skin override for the player side (VS Retrospecter extension).
Section format
Each entry in thenotes array is a section:
Array of individual notes. Each note is a JSON array:
[strum_time, direction, sustain_length] or [strum_time, direction, sustain_length, note_type].In legacy (non-
psych_v1) charts, controls how directions 0–7 are remapped to player vs opponent. In psych_v1 charts this field is ignored for direction calculation.Number of beats in this section.
If
true, all notes in this section use the alternate character animation.If
true, all notes in this section animate the girlfriend character instead.If
true, the bpm field takes effect at the start of this section.Note format
Each note insidesectionNotes is a JSON array:
| Index | Field | Type | Description |
|---|---|---|---|
| 0 | strum_time | number | Note spawn time in milliseconds |
| 1 | direction | number | 0–7. In psych_v1: 0–3 = player lanes (left/down/up/right), 4–7 = opponent lanes |
| 2 | sustain_length | number | Hold note length in milliseconds. 0 = tap note |
| 3 | note_type | string | Optional. Built-in values: "Alt Animation", "Hey!", "Hurt Note", "GF Sing", "No Animation". Any other string is a custom note type |
Events format
Events in the top-levelevents array use this structure:
[strum_time, [[name, value1, value2], ...]]. Multiple events can share the same strum time by listing them together in the inner array.
psych_v1 vs legacy format
psych_v1 (modern)
psych_v1 (modern)
Note directions are absolute:
0–3 always refer to player lanes and 4–7 always refer to opponent lanes, regardless of mustHitSection. Charts saved by Psych Engine 0.7+ use this format. The format field is set to "psych_v1" or "psych_v1_convert".Legacy (no format field)
Legacy (no format field)
Note directions are relative to
mustHitSection. When mustHitSection is false, directions 0–3 belong to the opponent and 4–7 belong to the player. The engine automatically converts these to the absolute layout before processing. Older Psych Engine charts and the base Friday Night Funkin’ charts use this format.Characters
Character files live atcharacters/{name}.json.
Example
Top-level fields
List of animation entries. See animation fields below.
Path to the sprite atlas relative to
images/, without extension (e.g. characters/BOYFRIEND). If empty, the engine falls back to images["baseSheet"].Multi-atlas map of sheet name to image path. The
"baseSheet" key is the primary atlas. Used when image is empty.Uniform scale multiplier applied to the character sprite.
How many steps the sing animation holds before the character returns to idle.
Health icon name. The engine resolves
images/icons/icon-{name}.png or images/icons/{name}.png.[x, y] spawn position of the character in the stage.[x, y] camera offset applied when the camera is focusing on this character.Mirror the sprite horizontally. Typically
true for the player character.Disable bilinear filtering. Use for pixel-art characters.
[r, g, b] color of this character’s side of the health bar.Custom vocals filename stem (without extension) inside
songs/{songName}/.Custom note skin path override (e.g.
notes/Notes_Ace).Custom health bar image name. Resolves to
healthBars/{name}/bar.png.Per-stage scale overrides. Keys are stage names (e.g.
"nightflaid": 0.774).Per-stage camera offset overrides. Keys are stage names, values are
[x, y].Animation fields
Each entry in theanimations array:
Internal animation name used by the engine (e.g.
idle, singLEFT, singDOWN, singUP, singRIGHT, singLEFTmiss).Sparrow XML prefix to match (trailing digits are stripped automatically). For example,
"BF NOTE LEFT0" matches frames named BF NOTE LEFT0000, BF NOTE LEFT0001, etc.Playback speed in frames per second.
Whether the animation loops when it reaches the last frame.
Optional subset of frame indices to play. Empty array plays all frames in sequence.
[x, y] pixel offset applied when this animation is active.Stages
Stage files live atstages/{name}.json.
Example
Stage fields
Subdirectory name under the search roots used to locate stage images. Leave empty to use the root
images/ path.Initial camera zoom when the song starts.
Disables antialiasing on all stage objects and characters for pixel-art stages.
[x, y] spawn position for the player character.[x, y] spawn position for the girlfriend character.[x, y] spawn position for the opponent character.If
true, the girlfriend character is not shown on this stage.[x, y] camera offset applied when the camera focuses on the player.[x, y] camera offset applied when the camera focuses on the opponent.[x, y] camera offset applied when the camera focuses on the girlfriend.Multiplier for how fast the camera moves toward its target.
Optional
[x, y] starting camera position. When set, the camera opens here instead of focusing on the opponent.If
true, disables beat-driven camera zoom pulses on this stage.Array of sprite objects to draw as the stage background and foreground.
Stage object fields
Object type identifier. Use
"sprite" for standard image sprites.Unique name for the object. Lua scripts can reference objects by this name.
Image path relative to
images/ (and optionally the stage directory), without extension.Horizontal position in the stage world.
Vertical position in the stage world.
[x, y] scale factors.[x, y] parallax scroll factors. 0.9 makes the object scroll slower than the camera (background depth effect).Opacity from
0.0 (transparent) to 1.0 (opaque).Hex color tint applied to the sprite (e.g.
"#FF0000" for red).Rotation in degrees.
Mirror the sprite horizontally.
Mirror the sprite vertically.
Enable bilinear filtering on this object. Set to
false for pixel art.Optional list of animation definitions for animated stage sprites. Uses the same structure as character animations (
anim, name, fps, loop, indices, offsets).Name of the animation to play when the stage loads (matches an
anim field in animations).