Metadata Guide
Metadata Guide
flo supports rich metadata including ID3v2.4-compatible fields and unique extensions for modern music production.
Quick Start
CLI
# Add basic metadata during encoding
reflo encode song.mp3 song.flo \
--title "My Song" \
--artist "Artist Name" \
--album "Album Name" \
--year 2026 \
--genre "Electronic" \
--track 1 \
--cover artwork.jpg
JavaScript
import { create_metadata_from_object, encode } from '@flo-audio/libflo';
const metadata = create_metadata_from_object({
title: "My Song",
artist: "Artist Name",
album: "Album Name",
year: 2026,
genre: "Electronic",
bpm: 128
});
const floData = encode(samples, 44100, 2, 16, metadata);
Rust
use libflo_audio::{FloMetadata, Encoder};
let mut meta = FloMetadata::new();
meta.title = Some("My Song".to_string());
meta.artist = Some("Artist Name".to_string());
meta.album = Some("Album Name".to_string());
meta.year = Some(2026);
let metadata = meta.to_msgpack()?;
let encoder = Encoder::new(44100, 2, 16);
let flo_data = encoder.encode(&samples, &metadata)?;
Standard Fields
Identification
| Field | Type | Description | ID3 |
|---|---|---|---|
title |
string | Track title | TIT2 |
subtitle |
string | Subtitle/description | TIT3 |
album |
string | Album name | TALB |
isrc |
string | International Standard Recording Code | TSRC |
Track Info
| Field | Type | Description | ID3 |
|---|---|---|---|
track_number |
number | Track position | TRCK |
track_total |
number | Total tracks | TRCK |
disc_number |
number | Disc number | TPOS |
disc_total |
number | Total discs | TPOS |
People
| Field | Type | Description | ID3 |
|---|---|---|---|
artist |
string | Main artist | TPE1 |
album_artist |
string | Album artist | TPE2 |
composer |
string | Composer | TCOM |
conductor |
string | Conductor | TPE3 |
lyricist |
string | Lyricist | TEXT |
remixer |
string | Remix artist | TPE4 |
Properties
| Field | Type | Description | ID3 |
|---|---|---|---|
genre |
string | Genre | TCON |
mood |
string | Mood (e.g., “energetic”) | TMOO |
bpm |
number | Beats per minute | TBPM |
key |
string | Musical key (e.g., “Am”, “C#”) | TKEY |
language |
string | Language code (e.g., “eng”) | TLAN |
Dates
| Field | Type | Description | ID3 |
|---|---|---|---|
year |
number | Release year | TYER |
recording_time |
string | Recording date/time | TDRC |
release_time |
string | Release date/time | TDRL |
Copyright
| Field | Type | Description | ID3 |
|---|---|---|---|
copyright |
string | Copyright notice | TCOP |
publisher |
string | Publisher name | TPUB |
Cover Art
Adding Cover Art (CLI)
reflo encode song.mp3 song.flo --cover cover.jpg
Adding Cover Art (JavaScript)
const coverData = await fetch('cover.jpg').then(r => r.arrayBuffer());
const metadata = create_metadata_from_object({
title: "My Song",
pictures: [{
mime_type: "image/jpeg",
picture_type: "cover_front",
description: "Album Cover",
data: new Uint8Array(coverData)
}]
});
Reading Cover Art
import { get_cover_art } from '@flo-audio/libflo';
const cover = get_cover_art(floData);
if (cover) {
const blob = new Blob([cover.data], { type: cover.mime_type });
const url = URL.createObjectURL(blob);
document.getElementById('cover-img').src = url;
}
Picture Types
| Type | Description |
|---|---|
cover_front |
Front album cover (most common) |
cover_back |
Back album cover |
artist |
Artist photo |
band |
Band photo |
file_icon |
Small icon |
leaflet_page |
Booklet page |
media |
CD/vinyl image |
flo Extensions
Section Markers
Mark song structure for visualization and navigation.
const metadata = create_metadata_from_object({
title: "My Song",
section_markers: [
{ timestamp_ms: 0, section_type: "intro" },
{ timestamp_ms: 15000, section_type: "verse", label: "Verse 1" },
{ timestamp_ms: 45000, section_type: "chorus" },
{ timestamp_ms: 75000, section_type: "verse", label: "Verse 2" },
{ timestamp_ms: 105000, section_type: "chorus" },
{ timestamp_ms: 135000, section_type: "bridge" },
{ timestamp_ms: 165000, section_type: "outro" }
]
});
Section Types:
intro,outroverse,pre_chorus,chorus,post_chorusbridge,breakdown,buildup,dropsolo,instrumentalsilence,other
BPM Map
Track tempo changes throughout the song.
const metadata = create_metadata_from_object({
bpm: 120, // Starting BPM
bpm_map: [
{ timestamp_ms: 0, bpm: 120 },
{ timestamp_ms: 60000, bpm: 130 }, // Speed up at 1:00
{ timestamp_ms: 120000, bpm: 120 } // Back to normal
]
});
Key Changes
Track key signature changes.
const metadata = create_metadata_from_object({
key: "Am", // Starting key
key_changes: [
{ timestamp_ms: 0, key: "Am" },
{ timestamp_ms: 90000, key: "C" }, // Modulate to C major
{ timestamp_ms: 150000, key: "Am" } // Return to A minor
]
});
Synchronized Lyrics
Lyrics with timestamps for karaoke-style display.
const metadata = create_metadata_from_object({
synced_lyrics: [{
language: "eng",
content_type: "lyrics",
lines: [
{ timestamp_ms: 5000, text: "First line of the song" },
{ timestamp_ms: 10000, text: "Second line here" },
{ timestamp_ms: 15000, text: "And the third line" }
]
}]
});
Reading synced lyrics:
import { get_synced_lyrics } from '@flo-audio/libflo';
const lyrics = get_synced_lyrics(floData);
if (lyrics) {
for (const line of lyrics) {
console.log(`${line.timestamp_ms}ms: ${line.text}`);
}
}
// Highlight current line during playback
function getCurrentLine(lyrics, currentTimeMs) {
for (let i = lyrics.length - 1; i >= 0; i--) {
if (lyrics[i].timestamp_ms <= currentTimeMs) {
return lyrics[i];
}
}
return null;
}
Waveform Data
Pre-computed waveform peaks for instant visualization.
const metadata = create_metadata_from_object({
waveform_data: {
peaks_per_second: 100,
channels: 2,
peaks: [
0.1, 0.2, 0.5, 0.8, 0.3, // ... peak values -1.0 to 1.0
]
}
});
Loudness Profile
Frame-by-frame LUFS measurements.
const metadata = create_metadata_from_object({
integrated_loudness_lufs: -14.0,
loudness_range_lu: 8.5,
true_peak_dbtp: -1.0,
loudness_profile: [
{ timestamp_ms: 0, lufs: -18.0 },
{ timestamp_ms: 1000, lufs: -14.0 },
{ timestamp_ms: 2000, lufs: -12.0 }
]
});
Creator Notes
Timestamped producer/artist commentary.
const metadata = create_metadata_from_object({
creator_notes: [
{ timestamp_ms: 0, text: "Recorded at Sunset Studios, LA" },
{ timestamp_ms: 45000, text: "This synth is a Moog Model D" },
{ timestamp_ms: 90000, text: "Guitar solo by John Smith" }
]
});
Animated Cover
GIF or WebP animated artwork.
const gifData = await fetch('cover.gif').then(r => r.arrayBuffer());
const metadata = create_metadata_from_object({
animated_cover: {
mime_type: "image/gif",
data: new Uint8Array(gifData),
duration_ms: 3000,
loop_count: 0 // 0 = infinite
}
});
Reading Metadata
JavaScript
import { get_metadata, get_cover_art, get_synced_lyrics } from '@flo-audio/libflo';
// Get all metadata
const meta = get_metadata(floData);
console.log(meta.title, meta.artist, meta.album);
// Get cover art
const cover = get_cover_art(floData);
// Get synced lyrics
const lyrics = get_synced_lyrics(floData);
Rust
use libflo_audio::get_metadata;
let meta = get_metadata(&flo_data)?;
if let Some(title) = &meta.title {
println!("Title: {}", title);
}
if let Some(markers) = &meta.section_markers {
for marker in markers {
println!("{:?} at {}ms", marker.section_type, marker.timestamp_ms);
}
}
CLI
# Human-readable
reflo metadata song.flo
# JSON format
reflo metadata song.flo --json
Best Practices
- Always include basic fields:
title,artist,album - Use high-quality cover art: JPEG or PNG, at least 500×500px
- Add section markers: Improves user navigation
- Include BPM: Helps DJs and music apps
- Use standard key notation: “Am”, “C#m”, “F”, etc.
- Keep synced lyrics accurate: Test with actual playback