Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/ivan-1f/phichain/llms.txt

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

The Official format is the native chart format used by Phigros. It’s optimized for game performance and compatibility, but has some limitations compared to more advanced formats.

File Structure

{
  "formatVersion": 3,
  "offset": 0.0,
  "judgeLineList": [
    // Line objects
  ]
}

Format Versions

Phichain supports two official format versions:
  • Version 1: Legacy format with encoded position events
  • Version 3: Current format with separate X/Y coordinates
When exporting, Phichain always generates version 3 for maximum compatibility.

Field Reference

OfficialChart

formatVersion
integer
required
Format version. Must be 1 or 3.
offset
number
required
Audio offset in seconds (not milliseconds like Phichain format).
judgeLineList
array
required
Array of judgment line objects.

Line Object

{
  "bpm": 120.0,
  "notesAbove": [],
  "notesBelow": [],
  "judgeLineMoveEvents": [],
  "judgeLineRotateEvents": [],
  "judgeLineDisappearEvents": [],
  "speedEvents": []
}
bpm
number
required
Base BPM for this line. All timing calculations use this value.
Unlike Phichain’s global BPM list, each line has its own fixed BPM in the official format.
notesAbove
array
required
Notes that appear above the judgment line.
notesBelow
array
required
Notes that appear below the judgment line.
judgeLineMoveEvents
array
required
Position events controlling line movement.
judgeLineRotateEvents
array
required
Rotation events.
judgeLineDisappearEvents
array
required
Opacity events (confusingly named - controls visibility, not just disappearing).
speedEvents
array
required
Note approach speed events.

Note Object

{
  "type": 1,
  "time": 2.5,
  "holdTime": 0.0,
  "positionX": 9.0,
  "speed": 1.0,
  "floorPosition": 0.0
}
type
integer
required
Note type:
  • 1 - Tap
  • 2 - Drag
  • 3 - Hold
  • 4 - Flick
time
number
required
Note timing in beats (not seconds). Calculated as:
time = beat * 1.875 / 60.0
holdTime
number
required
Hold duration in the same time units. 0.0 for non-hold notes.
positionX
number
required
Horizontal position in official units (0 to 18, where 9 is center).Conversion from pixels:
positionX = x / CANVAS_WIDTH * 18.0
where CANVAS_WIDTH = 1350
speed
number
required
Note approach speed multiplier.
For hold notes, this speed is adjusted based on the line’s speed events at the note’s timing.
floorPosition
number
required
Calculated floor position for note rendering. Auto-computed during export.

Move Event (Position)

{
  "startTime": 0.0,
  "endTime": 4.0,
  "start": 0.5,
  "start2": 0.5,
  "end": 0.7,
  "end2": 0.3
}
startTime
number
required
Event start time (same units as note time).
endTime
number
required
Event end time.
start
number
required
Starting X position in normalized coordinates (0.0 to 1.0, where 0.5 is center).
start2
number
required
Starting Y position (format version 3). Defaults to 0.0 in version 1.
end
number
required
Ending X position.
end2
number
required
Ending Y position (format version 3). Defaults to 0.0 in version 1.
In format version 1, X and Y are encoded into the start and end fields:
// Encoding
start = Math.round(x * 880) * 1000 + y * 530

// Decoding
x = Math.round(start / 1000) / 880
y = (start % 1000) / 530
Reference: phi-chart-render conversion code

Rotate/Opacity Event (Numeric)

{
  "startTime": 0.0,
  "endTime": 4.0,
  "start": 0.0,
  "end": 180.0
}
start
number
required
Starting value.
  • Rotation: degrees
  • Opacity: 0.0 (invisible) to 1.0 (fully visible)
end
number
required
Ending value.

Speed Event

{
  "startTime": 0.0,
  "endTime": 4.0,
  "value": 1.0,
  "floorPosition": 0.0
}
value
number
required
Speed multiplier in official units.Conversion from Phichain speed:
official_speed = phichain_speed / 9.0 * 2.0
floorPosition
number
required
Cumulative floor position. Auto-computed during export.

Complete Example

{
  "formatVersion": 3,
  "offset": 0.0,
  "judgeLineList": [
    {
      "bpm": 140.0,
      "notesAbove": [
        {
          "type": 1,
          "time": 0.0,
          "holdTime": 0.0,
          "positionX": 9.0,
          "speed": 1.0,
          "floorPosition": 0.0
        },
        {
          "type": 3,
          "time": 2.0,
          "holdTime": 1.0,
          "positionX": 12.0,
          "speed": 1.0,
          "floorPosition": 4.666
        }
      ],
      "notesBelow": [],
      "judgeLineMoveEvents": [
        {
          "startTime": 0.0,
          "endTime": 8.0,
          "start": 0.5,
          "start2": 0.5,
          "end": 0.5,
          "end2": 0.5
        }
      ],
      "judgeLineRotateEvents": [
        {
          "startTime": 0.0,
          "endTime": 8.0,
          "start": 0.0,
          "end": 360.0
        }
      ],
      "judgeLineDisappearEvents": [
        {
          "startTime": 0.0,
          "endTime": 8.0,
          "start": 1.0,
          "end": 1.0
        }
      ],
      "speedEvents": [
        {
          "startTime": 0.0,
          "endTime": 999999.0,
          "value": 1.0,
          "floorPosition": 0.0
        }
      ]
    }
  ]
}

Limitations

Linear Easing OnlyThe official format only supports linear interpolation between event values. When converting from Phichain/RPE:
  • Non-linear easing curves are approximated with 1/32 beat linear segments
  • This maintains visual accuracy but increases file size
Single BPM Per LineEach line has a fixed BPM. Charts with BPM changes are normalized during conversion:
  • First BPM value is used as the base
  • All beats are converted to equivalent timing at that BPM
No Advanced FeaturesThe following features are lost when converting to official format:
  • Child lines (flattened)
  • Curve note tracks
  • Event metadata
  • Line names

Quirks and Edge Cases

Floor positions are cumulative distances used for note rendering. Phichain computes them automatically:
floor_position += (end_time - start_time) * value / bpm * 1.875
For notes:
note.floor_position = event.floor_position + 
                      (note.time - event.start_time) * event.value / bpm * 1.875
Hold notes in the official format have their speed adjusted based on line speed events:
// During import
note.speed /= (line_speed / 9.0 * 2.0)

// During export  
note.speed *= (line_speed / 9.0 * 2.0)
This ensures holds render correctly across varying line speeds.

Conversion Notes

From Phichain

use phichain_chart::format::official::OfficialChart;
use phichain_chart::serialization::PhichainChart;
use phichain_chart::primitive::Format;

let phichain: PhichainChart = serde_json::from_str(&input)?;
let primitive = phichain.into_primitive()?;
let official = OfficialChart::from_primitive(primitive)?;

To Phichain

let official: OfficialChart = serde_json::from_str(&input)?;
let primitive = official.into_primitive()?;
let phichain = PhichainChart::from_primitive(primitive)?;

Source Code

The official format implementation is in:
  • phichain-chart/src/format/official.rs

Format Overview

Compare all supported formats

RPE Format

Re:PhiEdit format specification

Build docs developers (and LLMs) love