Skip to content
SPlayer-DevPublic

About

A framework-agnostic karaoke lyrics renderer for the browser, built on the native DOM API with zero runtime dependencies.

Topics

Resources

Stars

3 stars

Watchers

0 watching

Forks

Repository files navigation

lyric-dom

A framework-agnostic karaoke lyrics renderer for the browser, built on the native DOM API with zero runtime dependencies.

License TypeScript Zero Dependency

English | 简体中文

Installation

pnpm add lyric-dom
# or
npm install lyric-dom

Quick Start

Basic Setup

Import the renderer class along with its stylesheet:

import { LyricRenderer } from "lyric-dom";
import "lyric-dom/renderer.css";

const container = document.querySelector<HTMLDivElement>("#lyrics")!;

const renderer = new LyricRenderer(container, {
  playing: true,
  alignPosition: 0.35,
  onLineClick: (timeMs) => {
    player.seek(timeMs);
  },
});

// Pass raw lyric lines; scroll pre-roll is applied internally (enableScrollPreroll, on by default)
renderer.setLyrics(lines);

The container must have an explicit height: .lp-root uses contain: strict, so an auto-height container collapses to 0.

Playback Synchronization

Push playback progress on every animation frame (requestAnimationFrame). The audio timeupdate event fires only about 4 times per second, which makes the word-by-word sweep stutter:

// Update playback progress (milliseconds)
renderer.setCurrentTime(currentTimeMs);

// Switch play / pause state
renderer.setPlaying(isPlaying);

Resource Management & Page Visibility

// Suspend rendering loop when hidden (e.g. background tab or hidden window)
renderer.freeze();

// Resume rendering loop when returning to foreground
renderer.resume();

// Destroy renderer instance and clean up DOM and listeners
// (call it before creating a new renderer on the same container, otherwise the old render loop keeps running)
renderer.dispose();

Song Credits & Bottom Container

Add custom credit information, arranger credits, or extra spacing below the last lyric line:

const bottomEl = renderer.getBottomLineElement();
bottomEl.textContent = "Lyricist: ... | Composer: ...";

Styling with CSS Variables

Customize themes, font sizes, colors, and layout spacing through CSS custom properties:

#lyrics {
  --lp-color: #ffffff;
  --lp-padding-x: 1.5em;
  --lp-duet-color: #38bdf8;
  --lp-sub-opacity: 0.4;
  --lp-dot-size: 0.45em;
}

API Reference

LyricRenderer

new LyricRenderer(container, config?)

  • container: HTMLElement - Target container element to mount the renderer.
  • config?: Partial<RendererConfig> - Optional initial configuration.

Methods

Method Parameters Description
setLyrics(lines) lines: LyricLine[] Set lyric lines, rebuild DOM structure, and trigger entrance animation.
setCurrentTime(timeMs) timeMs: number Push current playback timestamp in milliseconds.
setPlaying(playing) playing: boolean Set playback state; pauses or resumes active word sweep animations.
setConfig(config) config: Partial<RendererConfig> Dynamically update renderer configurations.
freeze() - Suspend requestAnimationFrame loop and disconnect resize observers.
resume() - Resume requestAnimationFrame loop, re-observe dimensions, and resync animations.
getBottomLineElement() - Returns the bottom container element (.lp-credit) located below the last lyric line.
dispose() - Destroy the renderer instance, cancel animations, and remove all event listeners and DOM elements. Call it before creating a new renderer on the same container, otherwise the old instance's animation loop keeps running.

RendererConfig

Option Type Default Description
alignPosition number 0.35 Vertical alignment ratio of the active line in container (0 to 1, 0.35 = 35% from top).
alignment "auto" | "left" | "center" | "right" "auto" Horizontal alignment of lyric lines; non-auto overrides per-line alignment
playing boolean true Current playback state.
springConfig Partial<SpringParams> {} Custom spring physics parameters for scrolling and scaling animations.
wordFadeWidth number 0.5 Word-by-word gradient mask transition width ratio.
scrollResetDelay number 5000 Delay in milliseconds before auto-springing back after user manual scroll.
minInterludeGap number 4000 Minimum gap duration (ms) between lines to trigger interlude breathing dots.
breatheCycleTarget number 1500 Target breathing animation cycle duration (ms) for interlude dots.
alphaAttackSpeed number 16 Opacity transition attack speed when a line becomes active.
alphaReleaseSpeed number 7 Opacity transition release speed when a line becomes inactive.
inactiveAlpha number 0.2 Base opacity for inactive lines.
hidePassedLines boolean false Whether to fade out already played lines.
enableBlur boolean false Whether to enable distance-based blur effect on inactive lines.
enableWordHighlight boolean true Whether to enable word-by-word karaoke sweep highlighting.
enableFloatAnimation boolean false Whether to enable subtle upward floating animation on sung words.
enableEmphasizeEffect boolean false Whether to enable on-demand glow and motion emphasis for long notes.
emphasizeMinDuration number 1000 Minimum note duration in milliseconds required for emphasis.
showTranslation boolean true Whether to display translated lyrics.
showRomanization boolean true Whether to display romanized lyrics.
showWordRomanization boolean false Whether to display per-word romanization (romanWord) under each word.
showRuby boolean false Whether to display in-word ruby annotations.
enableScale boolean true Whether inactive lines are slightly scaled down.
enableBalancedWrap boolean true Balance wrapped lines to similar lengths, preferring breaks at punctuation, spaces and word boundaries (avoids a lone character on the last row).
bgAlwaysBelow boolean false Always place background vocal lines below their main line.
enableScrollPreroll boolean true Whether to apply applyScrollPreroll internally to lyrics passed to setLyrics.
scrollPrerollOptions Partial<ScrollPrerollOptions> {} Fine-tuning options for the internal scroll pre-roll.
seekBackwardThreshold number 100 Backward time jump (ms) recognized as a seek.
seekForwardThreshold number 2000 Forward time jump (ms) recognized as a seek.
onLineClick (timeMs: number) => void undefined Callback invoked when clicking a lyric line, receiving line start time in ms.

SpringParams

Spring animation parameters based on damped harmonic oscillator physics:

Parameter Type Default Description
mass number 0.9 Mass of the simulated object. Higher values yield greater inertia and slower acceleration.
damping number 15 Friction/damping coefficient controlling oscillation decay rate.
stiffness number 90 Spring stiffness. Higher values snap to target faster.
soft boolean false When true, forces over-damped mode (pure exponential decay without bounce).

Utilities

applyScrollPreroll(lines)

Pre-rolls line start times so that the view scrolls into place before each line begins singing. The renderer already applies it when enableScrollPreroll is true (default); call it yourself only with enableScrollPreroll: false, otherwise lines are pre-rolled twice:

  • Non-overlapping lines are advanced by up to 600 ms.
  • Overlapping lines are capped at 400 ms or 30% of the preceding line's duration.
import { applyScrollPreroll } from "lyric-dom";

renderer.setConfig({ enableScrollPreroll: false });
renderer.setLyrics(applyScrollPreroll(rawLines));

DEFAULTS

Exported constant containing all default configuration values of RendererConfig.

CSS Variables

All styling is configured through CSS custom properties with built-in fallbacks:

Variable Default Description
--lp-color #fff Main lyric text color.
--lp-padding-x 1em Horizontal padding of the inner lyric container.
--lp-line-padding 0.4em 1em Padding for each lyric line.
--lp-duet-color var(--lp-color, #fff) Text color for duet lines.
--lp-duet-indent 15% Indentation for duet layout (8% on screens <= 600px).
--lp-bg-font-size 0.75em Font size for background vocal lines (isBG).
--lp-bg-active-opacity 0.4 Opacity for active background vocal lines.
--lp-bg-tuck 0.85em Hidden offset a background float tucks into its main line.
--lp-hover-bg color-mix(...) Background color when hovering over a clickable line.
--lp-sub-font-size max(0.5em, 10px) Font size for translation and romanization sub-text.
--lp-sub-line-height 1.5em Line height for translation and romanization sub-text.
--lp-sub-opacity 0.2 Base opacity for translation and romanization sub-text (follows inactiveAlpha, incl. passed-line fading, when unset).
--lp-sub-color inherit Color for translation and romanization sub-text.
--lp-dot-size 0.45em Diameter of interlude breathing dots.
--lp-credit-opacity 0.3 Opacity for bottom song credits container.
--lyric-font-zh inherit Font family override for Chinese lyrics (:lang(zh)).
--lyric-font-ja inherit Font family override for Japanese lyrics (:lang(ja)).
--lyric-font-ko inherit Font family override for Korean lyrics (:lang(ko)).
--lyric-font-latin inherit Font family override for Latin lyrics (:lang(und-Latn)).

Data Models

type LyricLanguage = "ja" | "ko" | "zh-CN" | "und-Latn";

interface LyricLine {
  language?: LyricLanguage;
  words: LyricWord[];
  translatedLyric: string;
  romanLyric: string;
  startTime: number; // ms
  endTime: number; // ms
  isBG: boolean;
  isDuet: boolean;
}

interface LyricWord extends LyricSpan {
  romanWord?: string;
  obscene?: boolean;
  ruby?: LyricSpan[];
}

interface LyricSpan {
  startTime: number; // ms
  endTime: number; // ms
  word: string;
}

Acknowledgements

License

AGPL-3.0 © SPlayer-Dev

About

A framework-agnostic karaoke lyrics renderer for the browser, built on the native DOM API with zero runtime dependencies.

Topics

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages