npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@karaplay/kar-player

v1.3.2

Published

KAR (Karaoke) file player library with MIDI playback and lyric rendering support

Readme

@karaplay/kar-player

npm version License: MIT

🎤 Professional KAR (Karaoke) file player library with MIDI playback and advanced lyric rendering

A high-performance, production-ready karaoke player library built for React and Next.js applications. Features accurate timing, beautiful themes, and extensive customization options.

✨ Key Features

🎯 Core Capabilities

  • KAR & EMK Support - Play both KAR and EMK (Extreme Karaoke) files with automatic conversion
  • MIDI Playback - High-quality MIDI synthesis using SpessaSynth
  • Advanced Lyric Rendering - Professional-grade lyric display with multiple modes
  • Perfect Timing - Character-based progress calculation for pixel-perfect synchronization
  • Thai Language Optimized - Proper handling of Thai vowels and diacritics

🎨 Rendering Features

  • Line-Level Wipe Effect - Smooth gradient animation that follows actual word timing
  • Multiple Display Modes - Single-line, 2-line, 3-line, extreme-karaoke, and full modes
  • Beautiful Themes - Default, karaoke, minimal, extreme-karaoke, and text-only themes
  • Text-Only Mode - Export styled text for custom backgrounds
  • GPU Acceleration - Optimized for smooth 60fps rendering on low-end devices

⚙️ Configuration

  • Environment Variables - Configure via .env files (paths, colors, fonts, spacing)
  • Google Fonts - Auto-load from 5 Thai fonts (Kanit, Prompt, Anuphan, Chakra Petch, Sarabun)
  • Custom Themes - Create your own themes with CSS variables
  • Flexible Architecture - Works in browser and Node.js (server-side conversion)

🚀 Performance

  • Sliding Window Rendering - Minimal DOM elements (only visible lines)
  • RequestAnimationFrame - Smooth 60fps animation
  • Memoization - Prevents unnecessary re-renders
  • Page Visibility API - Auto-resume on tab switch

📦 Installation

npm install @karaplay/kar-player@latest buffer@^6.0.3

Next.js Configuration

If using Next.js, add webpack fallbacks to next.config.js:

module.exports = {
  webpack: (config, { isServer }) => {
    if (!isServer) {
      config.resolve.fallback = {
        ...config.resolve.fallback,
        fs: false,
        net: false,
        tls: false,
        buffer: require.resolve('buffer/'),
      };
    }
    return config;
  },
};

🎮 Quick Start

Basic React Usage

import { KaraokePlayer } from '@karaplay/kar-player';

function App() {
  const player = new KaraokePlayer({
    processorUrl: process.env.NEXT_PUBLIC_SPESSASYNTH_PROCESSOR_URL || '/spessasynth_processor.min.js',
    soundFont: process.env.NEXT_PUBLIC_SOUNDFONT_URL || '/assets/sounds/GeneralUserGS.sf3',
    soundBankName: 'main',
    lyricConfig: {
      displayMode: 'three-line',
      theme: 'karaoke'
    }
  });

  // Load and play
  await player.loadKarFile('/path/to/song.kar');
  player.play();

  return <div ref={player.lyricContainerRef} />;
}

React Hooks (Recommended)

import { useLyricRenderer } from '@karaplay/kar-player';

function LyricDisplay({ lyrics, currentTime }) {
  const { containerRef } = useLyricRenderer(lyrics, currentTime, {
    displayMode: 'two-line',
    theme: 'karaoke',
    highlightMode: 'line'
  });

  return <div ref={containerRef} className="lyric-container" />;
}

🎵 Rendering Modes

Line-Level Wipe Effect (v1.2.0+)

All rendering now uses line-level approach:

  • ONE element per line (no word spans)
  • Character-based progress - Accurate timing based on word positions
  • CSS gradient wipe - Smooth animation across entire line
  • Preserved spaces - All whitespace maintained
// Renders as:
<div class="kar-lyric-line kar-line-current" style="--line-progress: 0.5">
  สวัสดีครับ ยินดีต้อนรับ
</div>

Progress Calculation (v1.2.4+)

Accurate character-based progress:

// Example: 
// "Hello " (6 chars) at 1000ms
// "World" (5 chars) at 2000ms

// At 1500ms (middle of "Hello "):
// 3 chars highlighted (50% of 6)
// Progress: 3/11 = 0.27 (27%)

// At 2500ms (middle of "World"):
// 6 + 2.5 chars highlighted
// Progress: 8.5/11 = 0.77 (77%)

🎨 Display Modes

1. Two-Line Mode (two-line)

<useLyricRenderer displayMode="two-line" />
  • Shows 2 lines: current + next
  • Perfect for TV displays
  • Creates exactly 2 DOM elements

2. Three-Line Mode (three-line)

<useLyricRenderer displayMode="three-line" />
  • Shows 3 lines: prev + current + next
  • Best for desktop/mobile
  • Creates exactly 3 DOM elements

3. Extreme Karaoke Mode (extreme-karaoke)

<useLyricRenderer displayMode="extreme-karaoke" />
  • Alternating top/bottom display
  • Like professional karaoke boxes
  • Creates exactly 2 DOM elements

4. Single-Line Mode (single-line)

<useLyricRenderer displayMode="single-line" />
  • Shows only current line
  • Minimal UI
  • Creates exactly 1 DOM element

5. Full Mode (full)

<useLyricRenderer displayMode="full" />
  • Shows all lyrics
  • Scrollable view
  • Creates N DOM elements (N = visible lines in viewport)

🎨 Themes

Built-in Themes

// 1. Default Theme
<useLyricRenderer theme="default" />

// 2. Karaoke Theme (recommended)
<useLyricRenderer theme="karaoke" />

// 3. Minimal Theme
<useLyricRenderer theme="minimal" />

// 4. Extreme Karaoke Theme
<useLyricRenderer theme="extreme-karaoke" />

// 5. Text-Only Theme
<useLyricRenderer theme="text-only" />

Custom Theme

const myTheme = {
  name: 'my-theme',
  colors: {
    active: '#ff0000',
    inactive: '#666666',
    background: '#000000'
  },
  fonts: {
    family: 'Kanit, sans-serif',
    size: '2rem',
    weight: 600
  },
  spacing: {
    lineHeight: '1.5',
    wordSpacing: '-0.05em',
    letterSpacing: '0'
  }
};

<useLyricRenderer theme={myTheme} />

📝 Text-Only Mode (v0.5.1+)

Export styled text without container styling for custom backgrounds:

import { useLyricText } from '@karaplay/kar-player';

function CustomLyrics({ lyrics, currentTime }) {
  const { textElements } = useLyricText(lyrics, currentTime, {
    activeColor: '#ff0000',
    inactiveColor: '#666666'
  });

  return (
    <div style={{ background: 'url(my-bg.jpg)' }}>
      {textElements.map((line, i) => (
        <div 
          key={i}
          style={line.style}
          className={line.className}
        >
          {line.text}
        </div>
      ))}
    </div>
  );
}

🎭 EMK Support (v0.12.0+)

Client-Side (Browser)

import { convertEmkToKarBrowser } from '@karaplay/kar-player';

// Convert EMK to KAR in browser
const result = await convertEmkToKarBrowser('/path/to/song.emk');

if (result.success && result.karBuffer) {
  await player.loadKarBuffer(result.karBuffer);
  player.play();
}

Server-Side (Node.js)

// Import from /server subpath
import { convertEmkToKarServer } from '@karaplay/kar-player/server';

// Next.js API Route
export async function POST(request: Request) {
  const formData = await request.formData();
  const file = formData.get('file');
  
  const result = await convertEmkToKarServer(
    '/tmp/song.emk',
    { outputPath: '/tmp/song.kar' }
  );
  
  if (result.success) {
    return Response.json({ karPath: result.karPath });
  }
}

⚙️ Environment Configuration

.env Configuration

# SpessaSynth Processor (Required)
NEXT_PUBLIC_SPESSASYNTH_PROCESSOR_URL=/spessasynth_processor.min.js

# SoundFont (Required)
NEXT_PUBLIC_SOUNDFONT_URL=/assets/sounds/GeneralUserGS.sf3

# Legacy paths (deprecated, use above instead)
NEXT_PUBLIC_SPESSASYNTH_LIB_PATH=/spessasynth_lib.min.js
NEXT_PUBLIC_SPESSASYNTH_CORE_PATH=/spessasynth_core.min.js
NEXT_PUBLIC_DEFAULT_SOUNDFONT=/soundfont.sf2

# Lyric Styling
NEXT_PUBLIC_KAR_PLAYER_ACTIVE_COLOR=#ffeb3b
NEXT_PUBLIC_KAR_PLAYER_INACTIVE_COLOR=rgba(255,255,255,0.5)
NEXT_PUBLIC_KAR_PLAYER_LINE_HEIGHT=1.3
NEXT_PUBLIC_KAR_PLAYER_WORD_SPACING=-0.05em
NEXT_PUBLIC_KAR_PLAYER_LETTER_SPACING=-0.02em

# Google Fonts (Kanit, Prompt, Anuphan, Chakra Petch, Sarabun)
NEXT_PUBLIC_KAR_PLAYER_GOOGLE_FONT=Kanit
NEXT_PUBLIC_KAR_PLAYER_FONT_WEIGHT=400

Make sure these files are available in your public directory:

  • public/spessasynth_processor.min.js
  • public/assets/sounds/GeneralUserGS.sf3

Auto-Load Google Fonts

import { loadGoogleFont } from '@karaplay/kar-player';

// Auto-load from .env
loadGoogleFont(); // Uses NEXT_PUBLIC_KAR_PLAYER_GOOGLE_FONT

// Or specify manually
loadGoogleFont('Prompt', '600');

🚀 Performance Optimizations

1. Use Optimized Renderer

import { useLyricRendererOptimized } from '@karaplay/kar-player';

// Better performance with RAF + diffing
const { containerRef } = useLyricRendererOptimized(lyrics, currentTime, {
  displayMode: 'two-line',
  theme: 'karaoke'
});

2. Sliding Window Rendering

Automatically removes old DOM elements (only keeps visible lines):

// Automatically enabled in all renderers
// 2-line mode = exactly 2 elements in DOM
// 3-line mode = exactly 3 elements in DOM

3. GPU Acceleration

Built-in CSS optimizations:

.kar-lyric-line {
  will-change: background;
  transform: translateZ(0);
  backface-visibility: hidden;
  contain: layout style;
  content-visibility: auto;
}

4. Memoization

// All hooks use React.useMemo internally
const { textElements } = useLyricText(lyrics, currentTime, options);

📊 API Reference

KaraokePlayer Class

const player = new KaraokePlayer({
  processorUrl: string,  // Required: SpessaSynth processor URL
  soundFont: string | ArrayBuffer,  // Required: SoundFont URL or buffer
  soundBankName?: string,  // Optional: Default 'main'
  lyricConfig?: {
    displayMode?: 'single-line' | 'two-line' | 'three-line' | 'extreme-karaoke' | 'full',
    theme?: string | LyricTheme,
    highlightMode?: 'line' | 'word' | 'progressive' | 'none',
    autoScroll?: boolean,
    smoothScroll?: boolean
  }
});

// Methods
await player.loadKarFile(path: string): Promise<LoadKarFileResult>
await player.loadKarBuffer(buffer: ArrayBuffer): Promise<LoadKarFileResult>
player.play(): void
player.pause(): void
player.stop(): void
player.seekTo(time: number): void
player.setVolume(volume: number): void

Example with environment variables:

const player = new KaraokePlayer({
  processorUrl: process.env.NEXT_PUBLIC_SPESSASYNTH_PROCESSOR_URL!,
  soundFont: process.env.NEXT_PUBLIC_SOUNDFONT_URL!,
  soundBankName: 'main',
  lyricConfig: {
    displayMode: 'three-line',
    theme: 'karaoke'
  }
});

React Hooks

useLyricRenderer

const {
  containerRef,
  currentLineRef,
  forceUpdate,
  scrollToLine
} = useLyricRenderer(
  lyrics: LyricLine[],
  currentTime: number,
  options?: {
    displayMode?: 'single-line' | 'two-line' | 'three-line' | 'extreme-karaoke' | 'full',
    theme?: string | LyricTheme,
    highlightMode?: 'line' | 'word' | 'progressive' | 'none',
    textOnly?: boolean,
    disableBackground?: boolean,
    autoScroll?: boolean,
    smoothScroll?: boolean,
    classNames?: {
      container?: string,
      line?: string,
      current?: string,
      prev?: string,
      next?: string
    }
  }
);

useLyricText (Text-Only Mode)

const {
  textElements,
  getLineStyle,
  getLineClass,
  currentLineIndex,
  progress
} = useLyricText(
  lyrics: LyricLine[],
  currentTime: number,
  options?: {
    displayMode?: string,
    inactiveColor?: string,
    activeColor?: string,
    currentLineColor?: string,
    prevLineColor?: string,
    nextLineColor?: string,
    currentFontSize?: string,
    prevFontSize?: string,
    nextFontSize?: string
  }
);

// textElements structure:
interface LyricTextElement {
  lineIndex: number;
  position: 'prev' | 'current' | 'next';
  text: string;           // Full line text
  style: CSSProperties;   // Inline styles with gradient wipe
  className: string;      // kar-lyric-line kar-line-{position}
  progress: number;       // 0-1 for wipe effect
}

Progress Calculator Utilities

import { 
  calculateLineProgress,
  calculateLineProgressWordByWord,
  getProgressDebugInfo 
} from '@karaplay/kar-player';

// Character-based progress (recommended)
const progress = calculateLineProgress(
  parts: LyricPart[],
  currentTime: number
): number; // 0-1

// Word-by-word progress (no partial)
const progress = calculateLineProgressWordByWord(
  parts: LyricPart[],
  currentTime: number
): number; // 0-1

// Debug timing
const info = getProgressDebugInfo(
  parts: LyricPart[],
  currentTime: number
): {
  currentTime: number;
  totalChars: number;
  charsHighlighted: number;
  progress: number;
  currentPartIndex: number;
  currentPartText: string;
  currentPartTime: number;
};

📚 Documentation

🎯 Use Cases

1. Professional Karaoke App

import { KaraokePlayer } from '@karaplay/kar-player';

const player = new KaraokePlayer({
  processorUrl: '/spessasynth_processor.min.js',
  soundFont: '/assets/sounds/GeneralUserGS.sf3',
  soundBankName: 'main',
  lyricConfig: {
    displayMode: 'extreme-karaoke',
    theme: 'karaoke'
  }
});

2. Web Karaoke Platform

<useLyricRendererOptimized
  displayMode="three-line"
  theme="minimal"
  highlightMode="line"
/>

3. TV Karaoke Box

<useLyricRenderer
  displayMode="two-line"
  theme="extreme-karaoke"
  classNames={{ container: 'tv-display' }}
/>

4. Mobile Karaoke App

<useLyricRenderer
  displayMode="single-line"
  theme="minimal"
  textOnly={true}
/>

🛠️ Development

# Install dependencies
npm install

# Build
npm run build

# Test
npm test

# Test with coverage
npm run test:coverage

# Watch mode
npm run test:watch

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

📄 License

MIT © [Your Name]

🙏 Acknowledgments

  • SpessaSynth - MIDI synthesis engine
  • midi-file - MIDI file parsing
  • pako - Compression/decompression
  • iconv-lite - Encoding conversion
  • @karaplay/file-coder - EMK file support

📊 Stats

  • Bundle Size: ~140 KB (minified)
  • TypeScript: 100% type coverage
  • Tests: 147 unit tests
  • Browser Support: Chrome, Firefox, Safari, Edge (latest 2 versions)
  • Node.js: 16+ (for server-side conversion)

Built with ❤️ for the karaoke community 🎤🎵