🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

danmaku-lite

Package Overview
Dependencies
Maintainers
1
Versions
3
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

danmaku-lite

Framework-agnostic, player-agnostic danmaku (bullet chat) rendering engine with dual Canvas/DOM backends

latest
Source
npmnpm
Version
0.2.3
Version published
Maintainers
1
Created
Source

Danmaku Lite

npm version minzip size license TypeScript ESM only

Framework-agnostic, player-agnostic danmaku (bullet chat / 弹幕) rendering engine with dual Canvas/DOM backends. Zero dependencies. ESM only.

Features

  • Zero dependencies — pure TypeScript, no framework required
  • Player-agnostic — works with HTML5 <video>, libmpv, YouTube, custom players, WebRTC streams
  • Dual engine — Canvas 2D (high performance, 500+ concurrent danmaku) or DOM (simple, CSS-based)
  • 25 configurable parameters — font, stroke, speed, scroll gap, frame rate, area, overflow, and more
  • HiDPI — automatic devicePixelRatio scaling on Canvas
  • O(1) track allocation — free-track stacks with random-rotation fallback for even distribution
  • Configurable scroll gap — reference-width-based gap between scroll danmaku, scaled proportionally to container
  • ImageBitmap GPU cache — pre-rendered bitmaps with LRU eviction + alive-set leak guard
  • Seek-safe — automatic cursor rewind on backward seeks, visible cleanup on position jumps
  • Streaming loading — engine-managed incremental fetch via user-provided DataSourceAdapter; supports VOD, live, and local playback
  • send() API — temporarily display a single danmaku on send success; bypasses visibility caps
  • Tree-shakeable — ESM with TypeScript declarations, minified build included

Quick Start

Install

#npm
npm install danmaku-lite
# pnpm
pnpm add danmaku-lite
# yarn
yarn add danmaku-lite

Static Loading (VOD / local playback)

import { createEngine } from 'danmaku-lite'

const engine = createEngine('canvas', {
  container: document.getElementById('overlay')!,
  adapter: {
    get position() { return video.currentTime },
    get paused() { return video.paused },
  },
  fontFamily: 'Noto Sans SC, sans-serif',
  fontSize: 28,
  strokeWidth: 1.5,
  fps: 60,
  area: 0.75,
})

engine.load([
  { id: 1, text: 'Hello!', time: 5.0, mode: 1, color: 0xffffff },
  { id: 2, text: '前方高能', time: 12.0, mode: 5, color: 0xff4444 },
])

window.addEventListener('resize', () => engine.resize())

Streaming Loading (VOD / live streaming)

import { createEngine } from 'danmaku-lite'

const engine = createEngine('canvas', {
  container: document.getElementById('overlay')!,
  adapter: {
    get position() { return video.currentTime },
    get paused() { return video.paused },
  },
  // User-provided data source — engine decides when to fetch
  dataSource: {
    async fetch(start, end) {
      const res = await fetch(`/api/danmaku?start=${start}&end=${end}`)
      return res.json()
    },
  },
  preBuffer: 60,   // keep 60s of danmaku ahead of playhead
  leadTime: 30,    // evict items older than 30s behind playhead
})

// Engine fetches automatically — no need to call load().
// Call load() to seed initial data or replace content:
engine.load([{ id: 0, text: 'seeded!', time: 0, mode: 1, color: 0xffffff }])

Send Danmaku (on send success)

// User clicks send → your app POSTs to backend → on success:
engine.send({
  id: `user-${Date.now()}`,
  text: 'Hello from user!',
  time: video.currentTime,  // overridden to current position
  mode: 1,                   // 1=Scroll, 5=Top, 6=Bottom
  color: 0xff8800,
})

// The danmaku appears immediately, regardless of maxVisible / overflow settings.
// It follows the normal lifecycle (scrolls off-screen or expires) and is cleaned up.

Cleanup

engine.destroy()

API

createEngine(type, options)

Creates a danmaku engine instance.

  • type: 'canvas' | 'dom'
  • options: DanmakuOptions (see below)

Throws TypeError if container is a <video> element — wrap the video in a <div> instead.

DanmakuOptions

ParameterTypeDefaultApplies to
containerHTMLElementrequiredBoth
adapterPlayerAdapterrequiredBoth
enabledbooleantrueBoth
fpsnumber60Both
areanumber0.75Both — fraction of container height (0–1). Top/bottom danmaku each get 48% of this zone
fontFamilystring'sans-serif'Both
fontSizenumber25Both — base px; items can override via font_size
fontWeightstring'bold'Both
opacitynumber1.0Both (0–1)
paddingnumber4Canvas — text bitmap padding (px)
scrollGapnumber96Both — scroll danmaku gap at reference width 1920px, scaled proportionally
strokeWidthnumber1.25Both — outline width (px)
strokeColornumber0x000000Both — outline color (0xRRGGBB)
speednumber1.0Both — scroll speed multiplier
durationnumber4Both — fixed danmaku (mode 5/6) display time (s)
overflow'drop' | 'none''drop'Both — behavior when no track available. 'drop' discards, 'none' places randomly
maxVisiblenumber0Both — max simultaneous danmaku (0 = unlimited)
maxCachenumber500Canvas — ImageBitmap LRU cache size
preCacheCountnumber50Canvas — items to pre-render ahead of playhead
smoothingbooleantrueCanvas — imageSmoothingEnabled
willChangebooleantrueDOM — will-change: transform hint
useTextShadowbooleantrueDOM — text-shadow outline simulation
seekThresholdnumber0.2Both — minimum position jump (s) that triggers seek clear
onError(error: Error) => voidundefinedBoth — called on streaming fetch errors
dataSourceDataSourceAdapterundefinedBoth — enables streaming mode
preBuffernumber60Both — seconds ahead to pre-fetch (streaming mode)
leadTimenumber0Both — seconds behind to retain; older items evicted (0 = keep all)

PlayerAdapter

interface PlayerAdapter {
  readonly position: number   // current playback position in seconds
  readonly paused: boolean    // whether playback is paused
  readonly duration?: number  // total duration in seconds (optional)
}

DanmakuEngine

MethodDescription
load(items)Load/replace danmaku list (sorted by time). In streaming mode, resets the stream loader.
clear()Remove all visible danmaku and clear caches
resize()Recalculate dimensions — call on container resize or fullscreen change
destroy()Destroy engine, free all resources (idempotent)
send(item)Temporarily display a single danmaku at the current playback position. Bypasses maxVisible and overflow: drop. Use after a successful send to show the user's message immediately.
isDestroyedWhether destroy() has been called

Runtime setters — all trigger necessary side effects (cache invalidation, track recalculation, bitmap re-render):

setEnabled setFps setArea setOpacity setSpeed setFontFamily setFontSize setFontWeight setStrokeWidth setStrokeColor setPadding setScrollGap setDuration setOverflow setMaxVisible setMaxCache setPreCacheCount setSmoothing setWillChange setUseTextShadow

DanmakuItem

interface DanmakuItem {
  id: number | string     // unique identifier
  text: string            // display text (plain string, no HTML)
  time: number            // emission time in seconds (media time)
  mode: DanmakuMode       // 1 = scroll (right-to-left), 5 = top (fixed), 6 = bottom (fixed)
  color: number           // RGB as 0xRRGGBB
  font_size?: number      // per-item font size override (px)
}

DataSourceAdapter

User-provided adapter for streaming danmaku data. When provided via dataSource option, the engine manages data fetching automatically.

interface DataSourceAdapter {
  /**
   * Fetch danmaku items for a given time range.
   *
   * @param start - Start time in seconds (inclusive)
   * @param end   - End time in seconds (exclusive). May be Infinity
   *                for live streaming where the end is unknown.
   * @returns Items in this range. Need not be sorted. May be empty.
   */
  fetch(start: number, end: number): Promise<DanmakuItem[]>
}

Scenarios:

ScenarioAdapter behaviorConfig
Local playerfetch(0, duration) called once; all data loaded upfrontpreBuffer: 0
Online VODfetch() called in segments as playback progressespreBuffer: 60, leadTime: 0
Live streamingfetch(lastPos, Infinity) called continuously; returns current itemspreBuffer: 30, leadTime: 60

How it works:

  • Engine calls probe(position) every frame
  • If the current playback position + preBuffer extends beyond already-fetched ranges, a new fetch() is triggered
  • Fetched items are merged into the scheduler via scheduler.add()
  • On seek, in-flight fetches are cancelled and ranges are reset
  • If leadTime > 0, items older than position - leadTime are evicted from memory

Streaming + load() coexistence: Calling load() while streaming is active resets the stream loader (clears range cache, cancels in-flight requests) and replaces the scheduler pool with the explicit items. The stream loader resumes on the next tick from the current position.

Canvas vs DOM

CanvasDOM
Concurrent danmaku500+<200
OutlineNative strokeTexttext-shadow (8-direction)
HiDPIManual dpr scalingBrowser-native
GPU pathImageBitmap → drawImagetranslate3d composite layer
MemoryLower (shared canvas buffer)Higher (per-danmaku DOM node)
Bitmap cacheLRU with evictionN/A

Tree-shakeable Entry Points

npm package minimized gzipped size npm package minimized gzipped size npm package minimized gzipped size

Import only the engine you need for a smaller bundle:

// Full (both engines)
import { createEngine } from 'danmaku-lite'

// Canvas only
import { createEngine } from 'danmaku-lite/canvas'

// DOM only
import { createEngine } from 'danmaku-lite/dom'

All three entry points export the same public API. The per-engine entries omit the unused backend entirely.

Build Output

dist/
  index.js       ESM full (unminified + sourcemap)
  index.min.js   ESM full (minified + sourcemap)
  canvas.js      ESM canvas-only (unminified + sourcemap)
  canvas.min.js  ESM canvas-only (minified + sourcemap)
  dom.js         ESM dom-only (unminified + sourcemap)
  dom.min.js     ESM dom-only (minified + sourcemap)
  index.d.ts     TypeScript declarations

No CJS — this package is browser-only. ESM is supported by all modern bundlers (Vite, webpack 5, esbuild, Rollup, Parcel).

Browser Support

Chrome 71+, Firefox 76+, Safari 14.1+, Edge 79+.

Requires OffscreenCanvas (Canvas engine) or CSSStyleDeclaration + transform (DOM engine). All modern browsers since 2020.

License

MIT

Keywords

danmaku

FAQs

Package last updated on 04 May 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts