Synced lyrics provide a karaoke-style experience by displaying lyrics at precise timestamps during playback. This guide shows you how to create, sync, and manage lyrics for your tracks.
Prerequisites
An authenticated user account (see Authentication )
A valid JWT token
A track ID from a published release
The audio file for accurate timing
Plain text lyrics
Understanding synced lyrics
Synced lyrics in Lens Music consist of:
content : Array of lyric lines with optional timestamps
trackId : UUID of the associated track
language : Language code (defaults to “en”)
Each line can have a timestamp indicating when it should appear during playback.
Lyrics data structure
Lyrics are stored as a JSON array with this format:
{
"content" : [
{ "time" : "0.00" , "text" : "First line of lyrics" },
{ "time" : "3.50" , "text" : "Second line of lyrics" },
{ "text" : "Line without timestamp" }
],
"trackId" : "track-uuid" ,
"language" : "en"
}
The time field is optional. Lines without timestamps will still be stored but won’t sync to playback.
Creating unsynced lyrics
Start by creating lyrics without timestamps, which you can sync later.
Prepare lyrics content
Format your lyrics as an array of text objects: const lyricsContent = [
{ "text" : "Walking down the midnight street" },
{ "text" : "City lights beneath my feet" },
{ "text" : "Dreams are calling out my name" },
{ "text" : "Nothing's ever quite the same" }
];
Send create lyrics request
curl -X POST https://api.lensmusic.com/lyrics \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"content": [
{"text": "Walking down the midnight street"},
{"text": "City lights beneath my feet"}
],
"trackId": "track-uuid",
"language": "en"
}'
Handle the response
{
"message" : "Lyrics created successfully" ,
"data" : {
"id" : "lyrics-uuid" ,
"content" : [
{ "text" : "Walking down the midnight street" },
{ "text" : "City lights beneath my feet" },
{ "text" : "Dreams are calling out my name" },
{ "text" : "Nothing's ever quite the same" }
],
"trackId" : "track-uuid" ,
"language" : "en" ,
"createdAt" : "2026-03-04T10:30:00Z" ,
"updatedAt" : "2026-03-04T10:30:00Z"
}
}
Syncing lyrics with timestamps
Add precise timestamps to create synchronized lyrics.
Manual timestamp method
Create lyrics with pre-calculated timestamps:
const syncedLyrics = {
content: [
{ time: "0.00" , text: "Walking down the midnight street" },
{ time: "3.50" , text: "City lights beneath my feet" },
{ time: "7.20" , text: "Dreams are calling out my name" },
{ time: "11.00" , text: "Nothing's ever quite the same" }
],
trackId: 'track-uuid' ,
language: 'en'
};
const response = await fetch ( 'https://api.lensmusic.com/lyrics' , {
method: 'POST' ,
headers: {
'Authorization' : `Bearer ${ token } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ( syncedLyrics )
});
Interactive sync workflow
The Lens Music client provides an interactive syncing interface. Here’s how it works:
Upload audio file
Load the track’s audio file into the sync interface: const handleFileUpload = ( event ) => {
const file = event . target . files [ 0 ];
const url = URL . createObjectURL ( file );
setAudio ( url );
};
Navigate through lyrics
Use keyboard shortcuts to move through lyrics:
Arrow Up : Move to previous line
Arrow Down : Move to next line
Space : Sync current line to playback time
const handleKeyDown = ( event ) => {
if ( event . code === 'ArrowUp' && currentLineIndex > 0 ) {
setCurrentLineIndex ( prev => prev - 1 );
} else if ( event . code === 'ArrowDown' ) {
setCurrentLineIndex ( prev => prev + 1 );
} else if ( event . code === 'Space' && isPlaying ) {
event . preventDefault ();
handleSync ( currentLine , currentLineIndex );
}
};
Record timestamps
As the audio plays, press Space when each line should appear: const handleSync = ( line , index ) => {
setSyncedLyrics ( prev => {
const newSyncedLyrics = prev . filter ( item => item . index !== index );
return [ ... newSyncedLyrics , {
time: currentTime ,
line ,
index
}]. sort (( a , b ) => a . index - b . index );
});
};
Save synced lyrics
Once all timestamps are recorded, format and save: const formattedLyrics = syncedLyrics . map ( item => ({
time: item . time . toFixed ( 2 ),
text: item . line
}));
const response = await fetch ( `https://api.lensmusic.com/lyrics/ ${ lyricsId } ` , {
method: 'PATCH' ,
headers: {
'Authorization' : `Bearer ${ token } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
content: formattedLyrics
})
});
Fetching lyrics
Retrieve lyrics for tracks or specific lyrics by ID.
List lyrics for a track
curl -X GET "https://api.lensmusic.com/lyrics?trackId=track-uuid" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Get specific lyrics
curl -X GET https://api.lensmusic.com/lyrics/{lyricsId} \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Response:
{
"message" : "Lyrics fetched successfully" ,
"data" : {
"id" : "lyrics-uuid" ,
"content" : [
{ "time" : "0.00" , "text" : "Walking down the midnight street" },
{ "time" : "3.50" , "text" : "City lights beneath my feet" },
{ "time" : "7.20" , "text" : "Dreams are calling out my name" },
{ "time" : "11.00" , "text" : "Nothing's ever quite the same" }
],
"trackId" : "track-uuid" ,
"language" : "en" ,
"track" : {
"id" : "track-uuid" ,
"title" : "Midnight Dreams" ,
"duration" : 240
}
}
}
Language support
Set the lyrics language using ISO 639-1 language codes:
{
"language" : "en" , // English
"language" : "es" , // Spanish
"language" : "fr" , // French
"language" : "de" , // German
"language" : "ja" , // Japanese
"language" : "ko" // Korean
}
The language field defaults to “en” (English) if not specified.
Timestamps should be formatted as strings representing seconds with two decimal places:
// Good formats
"0.00" // Start
"3.50" // 3.5 seconds
"127.45" // 2 minutes 7.45 seconds
// Bad formats
3.5 // Number instead of string
"3:30" // Minutes:seconds format
"3500" // Milliseconds
Parameter Type Default Description pageinteger 0Zero-based page number sizeinteger 10Number of lyrics per page trackIdstring - Filter by track UUID
Best practices
Accurate timing Test synced lyrics with the actual audio to ensure timestamps are accurate and lyrics appear at the right moment.
Line breaks Keep lyrics lines to a readable length. Split long phrases into multiple lines for better display.
Language tags Always specify the correct language code for proper text rendering and language-specific features.
Empty lines Use empty text entries with timestamps for instrumental breaks: { "time" : "45.00" , "text" : "" }
Playback implementation
To display synced lyrics during playback:
const getCurrentLyricLine = ( currentTime , lyrics ) => {
// Find the lyric line for current playback time
let currentLine = null ;
for ( let i = 0 ; i < lyrics . content . length ; i ++ ) {
const line = lyrics . content [ i ];
const nextLine = lyrics . content [ i + 1 ];
const lineTime = parseFloat ( line . time || 0 );
const nextTime = nextLine ? parseFloat ( nextLine . time || Infinity ) : Infinity ;
if ( currentTime >= lineTime && currentTime < nextTime ) {
currentLine = line ;
break ;
}
}
return currentLine ;
};
// Usage in audio player
audioElement . addEventListener ( 'timeupdate' , () => {
const currentLine = getCurrentLyricLine ( audioElement . currentTime , lyrics );
displayLyric ( currentLine ?. text || '' );
});
Error handling
{
"message" : "Track not found"
}
The specified track ID doesn’t exist. Verify the track was created successfully.
Invalid content format (400)
{
"message" : "Validation failed" ,
"errors" : [
"content must be an array"
]
}
Ensure content is an array of objects with text and optional time fields.
Invalid language code (400)
{
"message" : "Invalid language code"
}
Use valid ISO 639-1 language codes (e.g., “en”, “es”, “fr”).
Next steps
API Reference View complete lyrics API documentation
Distributing releases Learn more about managing releases