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 🙏

© 2026 – Pkg Stats / Ryan Hefner

sweet-subtitle

v1.0.1

Published

Subtitle renderer and parser for web video: ASS/SSA, SRT, WebVTT, SBV, TTML/DFXP with React/Vue/Angular adapters and WASM effects

Readme

sweet-subtitle

npm version npm downloads GitHub stars license

Subtitle renderer and parser for web video, with full ASS/SSA support and multi-format parsing (SRT, WebVTT, SBV, TTML/DFXP), powered by Canvas 2D + Rust WASM.

One package for all usage modes:

  • Core / Vanilla JS: sweet-subtitle
  • React: sweet-subtitle/react
  • Vue: sweet-subtitle/vue
  • Angular: sweet-subtitle/angular

Features

  • Multi-format — SRT, WebVTT, ASS/SSA, SBV, TTML/DFXP
  • ASS advanced rendering — styles, positioning (\pos, \move, \org), rotation (\frz/frx/fry), animation (\t with accel), karaoke (\k/\kf/\ko), clip (\clip/\iclip), drawing commands (\p), fade (\fad/\fade), per-character alpha, border styles, etc.
  • Gaussian blur\blur and \be rendered via Rust WASM (3-pass box blur), with CSS filter fallback
  • Text decorations — underline (\u), strikeout (\s)
  • Scale transforms — horizontal scale (\fscx), vertical scale (\fscy)
  • Auto text wrapping — long lines wrap within video bounds
  • Encoding detection — UTF-8, UTF-16LE/BE (with BOM), GBK auto-detection
  • Rust WASM bundled — WASM binary ships inside the package, zero extra config for users
  • Lightweight — zero runtime dependencies, tree-shakeable ESM/CJS dual output
  • Resilient parsing — tolerates malformed ASS files (wrong Format lines, missing PlayRes, case-insensitive sections, BOM, flexible timestamps)

Format Support

| Format | Parse | Render | |--------|-------|--------| | SRT | Yes | Yes | | WebVTT | Yes | Yes | | ASS/SSA | Yes | Yes (advanced effects) | | SBV | Yes | Yes | | TTML/DFXP | Yes | Yes |

Install

npm install sweet-subtitle

The WASM binary is bundled inside the package — no extra setup needed.

Framework adapters are included in this same package via subpath imports.

GitHub Discoverability Checklist

  • Set repository topics: subtitle, ass, srt, webvtt, ttml, wasm, react, vue, angular, video.
  • Keep release notes updated for each npm publish (see changelog and release template below).
  • Pin a short usage snippet in repo description/About for quick conversion from visitors.

Release notes and history:

Quick Start

import { SweetSubtitle } from 'sweet-subtitle'

const video = document.querySelector('video')
const sub = new SweetSubtitle(video, {
  src: '/path/to/subtitle.ass',
})

sub.on('ready', () => console.log('Subtitle loaded'))
sub.on('error', (err) => console.error(err))

Or use Promise-based loading for clearer async flow:

import { SweetSubtitle } from 'sweet-subtitle'

const video = document.querySelector('video')!
const sub = new SweetSubtitle(video, { enableWasm: true })

await sub.loadFromUrl('/path/to/subtitle.ass')
// await sub.loadFromText(subtitleText)

Load from string

import { SweetSubtitle, decodeBuffer } from 'sweet-subtitle'

const buffer = await fetch('/sub.srt').then(r => r.arrayBuffer())
const content = decodeBuffer(buffer) // auto-detect encoding

const sub = new SweetSubtitle(video, { content })

Parser only

import { parse, detectFormat } from 'sweet-subtitle'

const format = detectFormat(content) // 'srt' | 'vtt' | 'ass' | 'sbv' | 'ttml'
const track = parse(content)

console.log(track.cues) // [{ id, start, end, text }, ...]

API

new SweetSubtitle(video, options?)

| Option | Type | Description | |--------|------|-------------| | src | string | URL to subtitle file | | content | string | Subtitle text content | | format | 'srt' \| 'vtt' \| 'ass' \| 'sbv' \| 'ttml' | Force format (auto-detected if omitted) | | offset | number | Time offset in seconds | | encoding | string | Force text decoding encoding for URL-loaded subtitles, e.g. utf-8, utf-16le, gbk | | fallbackEncodings | string[] | Custom decode fallback chain for URL-loaded subtitles (default: ['gbk', 'big5', 'shift_jis']) |

Instance Methods

| Method | Description | |--------|-------------| | loadFromUrl(url, format?) | Load subtitle from URL, returns Promise<void> | | loadFromText(content, format?) | Load subtitle from string, returns Promise<void> | | show() | Show subtitle overlay | | hide() | Hide subtitle overlay | | destroy() | Remove overlay and stop rendering | | setOffset(seconds) | Adjust subtitle timing | | once(event, callback) | Subscribe once and auto-unsubscribe | | on(event, callback) | Subscribe to events, returns unsubscribe | | off(event, callback) | Unsubscribe |

Encoding notes:

  • decodeBuffer auto-detects BOM (utf-8, utf-16le, utf-16be).
  • For no-BOM files, it applies UTF-16 heuristic detection and UTF-8 validation.
  • You can override detection using encoding and control fallback order using fallbackEncodings.

Cross-framework Usage

Vanilla JS

const sub = new SweetSubtitle(videoEl)
const unsubscribe = sub.on('error', console.error)

await sub.loadFromUrl('/demo.srt')

// cleanup
unsubscribe()
sub.destroy()

React

import { useEffect, useRef } from 'react'
import { useSweetSubtitle } from 'sweet-subtitle/react'

export function Player({ src }: { src: string }) {
  const videoRef = useRef<HTMLVideoElement>(null)
  const { error } = useSweetSubtitle(videoRef, { src, enableWasm: true })

  useEffect(() => {
    if (error) console.error(error)
  }, [error])

  return <video ref={videoRef} controls />
}

Vue 3

import { computed, ref, watch } from 'vue'
import { useSweetSubtitle } from 'sweet-subtitle/vue'

const videoRef = ref<HTMLVideoElement | null>(null)
const { error } = useSweetSubtitle(videoRef, {
  src: computed(() => props.subtitleUrl),
  enableWasm: true,
})

watch(error, (err) => {
  if (err) console.error(err)
})

Angular

import { Component } from '@angular/core'
import { SweetSubtitleDirective } from 'sweet-subtitle/angular'

@Component({
  selector: 'app-player',
  standalone: true,
  imports: [SweetSubtitleDirective],
  template: `
    <video
      controls
      sweetSubtitle
      [subtitleSrc]="'/assets/demo.ass'"
      [subtitleEnableWasm]="true"
      (subtitleError)="onSubtitleError($event)">
    </video>
  `,
})
export class PlayerComponent {
  onSubtitleError(err: Error) {
    console.error(err)
  }
}

Events

| Event | Payload | Description | |-------|---------|-------------| | ready | — | Track parsed and renderer initialized | | error | Error | Parse or load failure | | cuechange | SubtitleCue[] | Active cues changed |

Project Structure

sweet-subtitle/
├── packages/
│   ├── core/                  # npm package "sweet-subtitle"
│   │   ├── scripts/
│   │   │   └── setup-wasm.mjs # copies wasm-pack output into src before build
│   │   └── src/
│   │       ├── parser/        # SRT / VTT / ASS parsers
│   │       ├── renderer/      # Canvas 2D renderers (text + ASS)
│   │       ├── wasm/          # WASM bridge (lazy-load, JS fallback)
│   │       ├── SweetSubtitle.ts
│   │       ├── encoding.ts    # Encoding detection (UTF-8/16/GBK)
│   │       └── types.ts
│   └── wasm/                  # Rust WASM crate
│       └── src/
│           ├── blur.rs        # Gaussian blur (3-pass box blur)
│           └── drawing.rs     # ASS drawing command rasterizer
└── playground/                # Vite + React + Tailwind demo app

Development

# Install dependencies
pnpm install

# Build WASM (requires Rust + wasm-pack)
pnpm build:wasm

# Build the library
pnpm build

# Run playground
pnpm dev

# Type check
pnpm typecheck

# Run tests
pnpm test

# Dry run publish (recommended)
pnpm run release:dry

# Publish single package to npm (sweet-subtitle)
pnpm run release

# One-command publish (sync + check + build + dry-run + publish)
pnpm run release:one

Note: This repo now publishes one npm package only: sweet-subtitle. React/Vue/Angular adapters are included as subpath exports (sweet-subtitle/react, sweet-subtitle/vue, sweet-subtitle/angular).

pnpm build:wasm must be run at least once before pnpm build or pnpm run release. The prepublishOnly hook rebuilds WASM automatically on every publish.

Supported ASS Override Tags

| Tag | Description | |-----|-------------| | \b, \i, \u, \s | Bold, italic, underline, strikeout | | \fn, \fs, \fsp | Font name, size, spacing | | \fscx, \fscy | Scale X/Y | | \frz, \frx, \fry | Rotation Z/X/Y | | \c, \1c-\4c | Colors (primary, secondary, outline, shadow) | | \alpha, \1a-\4a | Alpha channels | | \bord, \shad | Border, shadow | | \blur, \be | Gaussian blur (WASM-accelerated) | | \pos, \move, \org | Position, animation, rotation origin | | \an, \a | Alignment (numpad / legacy) | | \fad, \fade | Fade in/out (2-param and 7-param) | | \clip, \iclip | Rectangular / drawing clip | | \t | Animation with accel curve | | \k, \kf, \ko, \K | Karaoke effects | | \p | Drawing mode | | \r | Style reset |

License

MIT