VYBE Open format

The .vybe file format, version 1

A .vybe file holds one song and everything that belongs with it: metadata, cover art, lyrics, credits, a waveform, the full song in one or more codecs and short preview clips. It is one small file that any app can read with a few lines of code.

This document is the full specification. The reference reader and writer is src/vybe.js in this kit (MIT license). Home page: https://vybemusic.com/open/

Property Value
Extension .vybe
MIME type application/vnd.vybe (suggested)
Byte order Little endian for every integer
Text UTF-8
Current version 1

1. Layout

A file is a fixed size header, an index with one entry per chunk, then the chunk bytes.

Offset Size Field
0 4 Magic, ASCII VYBE (56 59 42 45)
4 2 u16 version, 1
6 2 u16 file flags. bit0: the file has a SIGN chunk. Other bits are 0
8 4 u32 N, the number of chunks
12 28 x N Index, one 28 byte entry per chunk
12 + 28 x N ... Chunk bytes

Index entry (28 bytes):

Offset Size Field
+0 4 Chunk type, 4 printable ASCII characters (for example AOPS)
+4 4 u32 chunk flags, meaning depends on the type
+8 8 u64 offset of the chunk bytes, from the start of the file
+16 8 u64 length of the chunk bytes
+24 4 u32 CRC32 of the chunk bytes (IEEE 802.3 polynomial 0xEDB88320, the same CRC as zip and PNG)

Rules for the chunk area:

2. Chunk types

Type Content Flags
META UTF-8 JSON object with the song metadata (section 3). Required. 0
ARTW Cover image, at most 1400 px on the long side. bit0 = webp, otherwise jpeg
AOPS Full song, Ogg Opus (RFC 7845), 48 kHz, VBR. 160 kbps stereo or 96 kbps mono for lossless sources; lower for lossy sources (never more than about 1.15 x the source bitrate). 0
POPS Preview clip, Ogg Opus. Default 60 s with short fades in and out. 0
AAAC Full song, AAC-LC in ADTS frames, 48 kHz, 192 kbps stereo or 128 kbps mono. For AVPlayer and older players. 0
PAAC Preview clip, AAC-LC in ADTS. 0
AMP3 Full song, MP3. Written when the source is MP3 and it is kept as is (the original MP3 frames, ID3 tags removed), or when no other encoder was available. 0
PMP3 Preview clip, MP3, cut on frame boundaries (no fades). 0
PEAK Waveform: 2000 bytes, each 0..255, the peak level of each slice of the full song, normalized to the loudest point. 0
SIGN Signature written by the VYBE server. Opaque to everyone else. 0

Every chunk except META is optional. A valid file has at least one audio chunk (AOPS, AAAC or AMP3), but readers should cope with files that have none (they show metadata only).

The SIGN chunk

SIGN is written only by the VYBE server when a song is published through VYBE. Its content is not part of this specification and can change at any time. Readers treat it as opaque bytes: keep it when copying a file, never create one, and do not try to interpret it. File flag bit0 is set when the file has a SIGN chunk. A file without SIGN is still a complete, valid .vybe file. Editing any other chunk of a signed file makes the signature invalid, so tools that change a signed file should drop SIGN and clear bit0.

3. META

META is a JSON object. Writers include every key below; values are null when unknown. Readers must ignore keys they do not know.

{
  "format": "vybe", "v": 1,
  "title": "Neon Tide",
  "artist": {"name": "Ayo Lumen", "slug": "ayo-lumen"},
  "album": "Night Drive", "track_no": 3, "disc_no": 1,
  "duration": 215.42, "sample_rate": 44100, "channels": 2,
  "bpm": 122, "bpm_confidence": 0.93,
  "key": "A minor", "camelot": "8A", "key_confidence": 0.81,
  "energy": 0.72, "danceability": 0.88, "valence": 0.31,
  "loudness_lufs": -9.4, "loudness_range": 5.1, "true_peak_db": -0.8,
  "spectral_centroid": 2210.4,
  "vybes": ["latenight", "hype"], "vybe_scores": {"latenight": 0.91, "hype": 0.77},
  "mood": "Late Night", "genre": "House", "language": "en", "explicit": false,
  "lyrics": {"plain": "...", "synced": [{"t": 12.4, "line": "..."}]},
  "beats": {"first": 0.482, "period": 0.4918, "downbeat": 0.482},
  "sections": [{"start": 0, "end": 16.2, "label": "intro", "energy": 0.6}],
  "drop": 63.9,
  "preview": {"start": 47.2, "len": 60},
  "energy_curve": [0.41, 0.44],
  "credits": [{"name": "Ayo Lumen", "role": "producer"}],
  "rights": {"master_owner": null, "publishing_owner": null},
  "license": {"share_partners": true, "commercial": false, "territories": ["WW"]},
  "isrc": "USABC2600001", "upc": null,
  "created": "2026-10-21T09:00:00.000Z",
  "encoder": "VYBE Studio 1",
  "source": {"format": "wav", "sample_rate": 44100, "bits": 24, "bytes": 38211044, "sha256": "..."},
  "fingerprint": "vf1.AbC...",
  "streams": {
    "AOPS": {"codec": "opus", "kbps": 160, "container": "ogg", "sample_rate": 48000, "channels": 2, "pre_skip": 312},
    "POPS": {"codec": "opus", "kbps": 160, "container": "ogg", "preview": true, "start": 47.2, "len": 60}
  }
}

Fields

Field Type Notes
format, v string, number Always "vybe" and 1.
schema string "1.1" for files with the full song information (section 4). Missing means "1.0".
title string Song title.
artist object {name, slug}. Older or hand made files may hold a plain string; readers accept both. Schema 1.1 adds artists and display_artist (section 4).
album, track_no, disc_no string, number, number
duration number Seconds, of the full song.
sample_rate, channels number Of the source recording (streams are always 48 kHz).
bpm, bpm_confidence number Tempo and a 0..1 confidence.
key, camelot, key_confidence string, string, number For example "A minor", "8A", 0..1.
energy, danceability, valence number 0..1.
loudness_lufs, loudness_range, true_peak_db number Integrated loudness (EBU R128), loudness range in LU, true peak in dBTP.
spectral_centroid number Brightness, in Hz.
vybes, vybe_scores, mood array, object, string 1 to 3 VYBE ids, best first, their 0..1 scores, and the display name of the first one.
genre, language, explicit string, string, boolean language is an ISO 639-1 code.
lyrics object {plain, synced:[{t, line}]}. t in seconds. Schema 1.1 adds sections and structure (section 4).
beats object {first, period, downbeat} in seconds: first beat, beat length, first downbeat.
sections array {start, end, label, energy}. label is one of intro, verse, chorus, bridge, breakdown, drop, outro.
drop number Seconds, the main drop, or null.
preview object {start, len} in seconds: the window the preview clips were cut from.
energy_curve array 200 numbers 0..1 across the song.
credits array {name, role}. Schema 1.1 adds role_label and a fixed role vocabulary (section 4).
rights object {master_owner, publishing_owner}.
license object {share_partners, commercial, territories}. Apps must check share_partners before playing a VYBE catalog song outside VYBE.
isrc, upc string
created string ISO 8601 time the file was made.
encoder string The tool that made the file, for example "VYBE Studio 1", "vybe-format 1.1", "vybe-cli 1.1".
source object {format, sample_rate, bits, bytes, sha256} of the original upload.
fingerprint string vf1. plus base64url of one little endian u16 per second (12 chroma shape bits and 4 band energy trend bits). Compare two with VybeAnalyze.similarity(a, b): 0..1, 0.9 and up means the same recording.
streams object One entry per audio chunk type: codec, kbps, and container, preview, start, len, pre_skip, original, fades where they apply.

VYBE ids:

id Name id Name
euphoric Happy dreamy Sleep
chill Chill hype Party
latenight Late Night deep Sad
sunset Road Trip fuel Workout
focus Focus crush Love

4. Song information (schema 1.1)

Files made with version 1.1 of the kit, and by VYBE Studio, carry the full song information: every artist and what they did, the release, the rights lines and the lyrics with their parts. These fields live in META like all the others, so the file format version stays 1. A file that has them says "schema": "1.1".

Old files keep working. Files from before 1.1 have no schema key (read it as "1.0"), only artist, maybe credits and lyrics.plain. Readers must handle both. The easy way is VybeInfo.read(meta) from src/vybe-info.js, or file.info() from src/vybe.js: both give the same shape for any file, old or new. Writers of schema 1.1 still write artist (see below) so that older readers show the right name.

Example

{
  "format": "vybe", "v": 1, "schema": "1.1",
  "title": "Midnight Drive", "version": "Radio Edit",
  "artists": [
    {"name": "Nia Vale", "role": "main", "slug": "nia-vale"},
    {"name": "Kai Moro", "role": "main", "slug": "kai-moro"},
    {"name": "Juno", "role": "featured", "slug": "juno"}
  ],
  "display_artist": "Nia Vale & Kai Moro feat. Juno",
  "artist": {"name": "Nia Vale & Kai Moro feat. Juno", "slug": "nia-vale"},
  "credits": [
    {"name": "Nia Vale", "role": "lyricist", "role_label": "Lyricist"},
    {"name": "Sam Ortiz", "role": "producer", "role_label": "Producer"},
    {"name": "Ivy Chen", "role": "mastering_engineer", "role_label": "Mastering engineer"},
    {"name": "Omar Diaz", "role": "guitar", "role_label": "Guitar"}
  ],
  "people": {
    "main_artists": ["Nia Vale", "Kai Moro"], "featured_artists": ["Juno"],
    "lyricists": ["Nia Vale"], "composers": [], "singers": [],
    "producers": ["Sam Ortiz"], "engineers": ["Ivy Chen"]
  },
  "album": "Night Roads", "album_artist": null,
  "release": {
    "title": "Night Roads", "type": "album", "date": "2025-06-13", "year": 2025, "original_year": null,
    "track_no": 2, "track_total": 10, "disc_no": 1, "disc_total": 1,
    "label": "Moonlit Records", "upc": "0123456789012", "catalog_no": "MLR-022"
  },
  "label": "Moonlit Records", "publisher": null, "year": 2025,
  "copyright": {"c": "2025 Nia Vale", "p": "2025 Moonlit Records"},
  "isrc": "USABC2512345", "iswc": null, "upc": "0123456789012",
  "genre": "R&B", "subgenres": ["Alternative R&B"], "language": "en", "explicit": false,
  "comment": null, "recorded": null, "tags": [],
  "declared": {"bpm": 118, "key": "A major"},
  "lyrics": {
    "plain": "yeah yeah\n\nCity lights are calling out my name\n...",
    "synced": [{"t": 12.4, "line": "City lights are calling out my name", "section": "Verse 1"}],
    "sections": [
      {"type": "intro", "label": "Intro", "n": null, "performer": null, "start": 1.2, "end": 12.4, "lines": ["yeah yeah"], "inferred": false},
      {"type": "verse", "label": "Verse 1", "n": 1, "performer": "Nia", "start": 12.4, "end": 30.1, "lines": ["City lights are calling out my name", "..."], "inferred": false},
      {"type": "hook", "label": "Hook", "n": null, "performer": null, "start": 30.1, "end": 45.8, "lines": ["We ride we ride into the night", "..."], "inferred": false}
    ],
    "structure": ["Intro", "Verse 1", "Hook"],
    "language": "en",
    "writers": ["Nia Vale"]
  },
  "links": {}
}

Fields

Field Type Notes
schema string "1.1". Missing means "1.0".
title string The title only, without "(feat. X)", "[Prod. Y]" or the version.
version string The version of the recording, for example "Radio Edit", "Acoustic", "Remix".
artists array {name, role, slug}. role is main, featured or remixer. In the order they are credited.
display_artist string How to show the artists in one line: "A & B feat. C".
artist object {name, slug}, kept for older readers: name is the display artist, slug is the first main artist.
credits array {name, role, role_label}. role is an id from the table below; a role that is not in the table is kept as written. role_label is the English label, so simple readers can show it as is. One person can have several credits.
people object The same credits as lists of names, for quick use: main_artists, featured_artists, lyricists (lyricist and songwriter), composers (composer and songwriter), singers (vocals, rapper, background vocals, performer), producers (all producer roles), engineers (all engineer roles).
album, album_artist string album mirrors release.title.
release object {title, type, date, year, original_year, track_no, track_total, disc_no, disc_total, label, upc, catalog_no}. type is single, ep, album, compilation or null. date is YYYY-MM-DD.
track_no, disc_no number Mirror release.track_no and release.disc_no (as in schema 1.0).
label, publisher string Record label, and the music publisher when known.
year number Release year. Mirrors release.year.
copyright object {c, p}: the copyright line for the song (written with a (c) sign) and for the recording (written with a (p) sign), without the sign. Null when unknown.
isrc, iswc, upc string Recording code, work code, release barcode.
genre, subgenres string, array Main genre and up to 5 more.
language, explicit string, boolean
comment, recorded, tags string, string, array A free note, where it was recorded, free tags.
declared object {bpm, key} as the source file's tags said them, or null. The measured bpm and key stay the main values.
lyrics object See below. Null when the song has no lyrics.
links object Free links, for example {"video": "https://..."}.

lyrics in schema 1.1:

Field Type Notes
plain string The words, without section markers. Blank lines between parts.
synced array Timed lines {t, line, section}. t in seconds, section is the label of the part the line is in (when known). Empty when there are no timed lyrics.
sections array The parts of the song in order: {type, label, n, performer, start, end, lines, inferred}. type is an id from the section table, label is what to show ("Verse 2"), n the number, performer who sings it (from markers like [Verse 1: Nia]), start and end in seconds when timed lyrics place it (else null), lines the lines of that part. inferred is true when the part was found from repeats instead of written markers.
structure array The section labels in order, for example ["Intro", "Verse 1", "Pre Hook", "Hook", "Verse 2", "Hook", "Outro"].
language, writers string, array Language of the words, and the lyricist and songwriter names.

The sections key at the top level of META (from the audio analysis, with intro, verse, chorus, drop and so on, measured from the sound) is a different thing and stays as it was.

Credit roles

Writers map free text roles onto these ids ("Produced by", "Prod.", "Beat maker" become producer; "Mixed by" becomes mixing_engineer, and so on).

id Label id Label
main_artist Main artist programmer Programmer
featured_artist Featured artist conductor Conductor
remixer Remixer dj DJ
vocals Singer guitar Guitar
rapper Rapper bass Bass
background_vocals Background vocals drums Drums
performer Performer keys Keys
lyricist Lyricist piano Piano
composer Composer synth Synth
songwriter Songwriter strings Strings
producer Producer percussion Percussion
co_producer Co-producer saxophone Saxophone
executive_producer Executive producer trumpet Trumpet
additional_producer Additional producer violin Violin
mixing_engineer Mixing engineer cello Cello
mastering_engineer Mastering engineer instruments Instruments
recording_engineer Recording engineer artwork Artwork
engineer Engineer video_director Video director
arranger Arranger

Lyric sections

Artists say hook, so the chorus is called hook. Markers like [Chorus], (Pre-Chorus), Refrain:, [Hook x2], {Bridge} or **Drop** are all read.

id Label id Label
intro Intro breakdown Breakdown
verse Verse interlude Interlude
pre_hook Pre Hook refrain Refrain
hook Hook instrumental Instrumental
post_hook Post Hook spoken Spoken
drop Drop skit Skit
build Build outro Outro
bridge Bridge

Lyrics text with markers

Tools that let people edit lyrics use one plain text form: each part starts with its label in square brackets on its own line, with an optional performer.

[Intro]
yeah yeah

[Verse 1: Nia]
City lights are calling out my name
Every road is running through the rain

[Pre Hook]
Hold on hold on

[Hook]
We ride we ride into the night

VybeInfo.parseLyrics(text) turns this into sections, and VybeInfo.lyricsText(sections) turns sections back into this text. When the text has no markers, VybeInfo.inferSections(text) finds the parts: the stanza that repeats most is the hook, a different repeated stanza that always comes right before it is the pre hook, and a short first or last stanza is the intro or outro.

Where writers find the information

The converter in this kit (VybeInfo.capture) reads, in this order:

5. Picking a stream

A file can carry the same song in up to three codecs. Readers pick the first one they can play, in this order:

  1. Opus (AOPS, preview POPS): smallest for the same quality. Plays in Chrome, Edge, Firefox, Android and recent Safari.
  2. AAC (AAAC, preview PAAC): for iOS AVPlayer, older Safari and hardware players.
  3. MP3 (AMP3, preview PMP3): plays everywhere.

In a browser, test Opus with audio.canPlayType('audio/ogg; codecs="opus"'). WebM Opus support alone is not enough. Vybe.canPlay(codec) does this for you, and file.audio() returns the best playable stream.

Previews: when a preview would cover 80% or more of the song, writers skip the preview chunks. Readers then play the full stream from meta.preview.start for meta.preview.len seconds (the SDK returns fullStream: true).

MIME types for the stream bytes:

Chunks MIME type
AOPS, POPS audio/ogg; codecs=opus
AAAC, PAAC audio/aac
AMP3, PMP3 audio/mpeg
ARTW image/webp (flag bit0) or image/jpeg
META application/json

6. Reading only what you need (HTTP Range)

The whole index sits in the first 12 + 28 x N bytes. The first 1024 bytes always hold the index of a file with up to 36 chunks, and usually META too. A player that wants a preview card does not need to download the song:

  1. GET song.vybe with Range: bytes=0-1023. Check the magic and version, read N and the index. If 12 + 28 x N is more than you received, request bytes=0-(12 + 28 x N - 1).
  2. Find META in the index. If offset + length is inside the bytes you already have, parse it from there; otherwise request exactly bytes=offset-(offset + length - 1).
  3. Request only the chunks you need (for example ARTW, PEAK and POPS), one Range request each. Check each chunk against its CRC32 if integrity matters.

If the server ignores Range and answers 200 with the whole file, use the whole file. Vybe.open(url) in src/vybe.js implements all of this and caches chunks. Servers can also proxy a single stream: remote.range('AOPS') returns {start, end} for one upstream Range request.

7. Writing

8. Versioning and compatibility

9. Verifying a file

A file is valid when:

  1. It starts with VYBE and its version is supported.
  2. The index fits in the file, and every chunk lies inside the file after the index, without overlapping another chunk.
  3. The first chunk is META and it is a UTF-8 JSON object.
  4. Every chunk matches its CRC32.
  5. File flag bit0 is set exactly when there is a SIGN chunk.

vybe verify file.vybe from this kit checks all of these and exits with code 1 when the file is not valid.

This specification and the reference code are open source under the MIT license. Download them with the .vybe kit, or convert a song to see a real file.