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

play-pcm

v1.0.3

Published

A highly customizable PCM audio player with React component support. Play, manipulate, and export PCM audio with pitch control, speed adjustment, seeking, waveform visualization, and more.

Readme

play-pcm

A highly customizable PCM audio player with React component support. Play, manipulate, and export PCM audio with pitch control, speed adjustment, seeking, waveform visualization, and more.

npm version License: MIT

Features

  • 🎵 Play PCM Audio - Play Float32Array audio samples directly
  • ⏯️ Full Playback Controls - Play, pause, stop, seek, skip forward/backward
  • 🎚️ Volume Control - Adjust volume with mute/unmute support
  • Tempo/Speed Control - Adjust playback speed (0.25x to 4x) without affecting pitch
  • 🎼 Pitch Control - Adjust pitch in semitones (-12 to +12) without affecting speed
  • 🎹 Fine Pitch Control - Adjust pitch in cents for precise tuning
  • 🔁 Loop Mode - Enable/disable looping
  • 🌊 Waveform Visualization - Generate and display audio waveforms
  • 📊 Audio Analysis - Real-time frequency and time-domain analysis
  • 🎛️ Equalizer - Multi-band equalizer support
  • 📁 Export Audio - Export to WAV, WebM, or OGG formats
  • ⬇️ Download - Download audio files directly
  • 🎨 Customizable UI - Fully themeable React component
  • ⌨️ Keyboard Shortcuts - Built-in keyboard controls
  • Accessible - ARIA labels and keyboard navigation
  • 📱 Responsive - Mobile-friendly design
  • 🔧 Audio Utilities - Comprehensive audio processing functions
  • 📦 Tree-shakeable - Import only what you need
  • 💪 TypeScript - Full TypeScript support with comprehensive types

Installation

npm install play-pcm
yarn add play-pcm
pnpm add play-pcm

Quick Start

Simple Playback (No React)

import { playPCM } from 'play-pcm';

// Play PCM audio with default settings
const samples = new Float32Array([...]); // Your PCM samples
const player = playPCM(samples);

// Or with options
const player = playPCM(samples, {
  sampleRate: 16000,
  playbackRate: 1.3,
  volume: 0.8,
});

Using the PCMPlayer Class

import { PCMPlayer, createPCMPlayer } from 'play-pcm';

// Create a player instance
const player = createPCMPlayer({
  sampleRate: 16000,
  volume: 1,
  playbackRate: 1,
});

// Load samples
player.loadSamples(samples);

// Control playback
await player.play();
player.pause();
player.stop();

// Seek
player.seek(5); // Seek to 5 seconds
player.seekPercent(50); // Seek to 50%

// Volume
player.setVolume(0.5);
player.mute();
player.unmute();
player.toggleMute();

// Speed/Tempo (without affecting pitch)
player.setTempo(1.5);  // 1.5x speed

// Pitch (without affecting speed)
player.setPitch(2);       // +2 semitones higher
player.setPitchCents(50); // Fine-tune by +50 cents

// Loop
player.setLoop(true);
player.toggleLoop();

// Events
player.on('timeUpdate', ({ currentTime, duration, progress }) => {
  console.log(`Progress: ${progress * 100}%`);
});

player.on('ended', () => {
  console.log('Playback ended');
});

// Export audio
const blob = await player.exportAudio({ format: 'wav' });
await player.downloadAudio('my-audio.wav');

// Cleanup
player.destroy();

Speed and Pitch Control

The player provides independent control over speed (tempo) and pitch:

// Speed/Tempo - changes how fast audio plays WITHOUT affecting pitch
player.setTempo(1.5);     // 1.5x speed, same pitch
player.setTempo(0.5);     // 0.5x speed (slower), same pitch

// Pitch - changes pitch WITHOUT affecting speed
player.setPitch(3);       // +3 semitones (higher pitch)
player.setPitch(-2);      // -2 semitones (lower pitch)

// Fine-grained pitch control in cents (100 cents = 1 semitone)
player.setPitchCents(50); // +50 cents (half a semitone)

// Combined: slow tempo + higher pitch
player.setTempo(0.75);
player.setPitch(2);

// Get current values
const tempo = player.getTempo();
const pitch = player.getPitch();
const cents = player.getPitchCents();
const totalCents = player.getTotalPitchCents(); // semitones * 100 + cents

Note: playbackRate and tempo are aliases when preservePitch is true. When preservePitch is false, changing playback rate also changes pitch (like vinyl speed change).

React Component

import { PCMPlayerComponent } from 'play-pcm';

function App() {
  const samples = new Float32Array([...]); // Your PCM samples

  return (
    <PCMPlayerComponent
      samples={samples}
      options={{
        sampleRate: 16000,
        tempo: 1,        // Speed without pitch change
        pitchSemitones: 0,  // Pitch adjustment
      }}
      showWaveform={true}
      showSpeedControl={true}
      showVolumeControl={true}
      showPitchControl={true}
      showDownloadButton={true}
      title="My Audio"
      artist="Artist Name"
      onEnded={() => console.log('Ended')}
      onTimeUpdate={(time, duration) => console.log(time, duration)}
    />
  );
}

React Hook

import { usePCMPlayer } from 'play-pcm';

function CustomPlayer({ samples }: { samples: Float32Array }) {
  const {
    state,
    currentTime,
    duration,
    progress,
    volume,
    tempo,        // Current tempo
    pitch,        // Current pitch in semitones
    pitchCents,   // Current pitch cents
    isReady,
    play,
    pause,
    toggle,
    seek,
    setVolume,
    setTempo,        // Set tempo (speed without pitch change)
    setPlaybackRate, // Alias for setTempo
    setPitch,        // Set pitch in semitones
    setPitchCents,   // Set fine-grained pitch in cents
    downloadAudio,
    waveformData,
  } = usePCMPlayer(samples, { 
    sampleRate: 16000,
    tempo: 1,
    pitchSemitones: 0,
  });

  return (
    <div>
      <button onClick={toggle}>
        {state === 'playing' ? 'Pause' : 'Play'}
      </button>
      
      {/* Tempo slider */}
      <label>
        Speed: {tempo}x
        <input
          type="range"
          min={0.25}
          max={2}
          step={0.1}
          value={tempo}
          onChange={(e) => setTempo(parseFloat(e.target.value))}
        />
      </label>
      
      {/* Pitch slider */}
      <label>
        Pitch: {pitch > 0 ? '+' : ''}{pitch} semitones
        <input
          type="range"
          min={-12}
          max={12}
          step={1}
          value={pitch}
          onChange={(e) => setPitch(parseInt(e.target.value))}
        />
      </label>
      
      <span>{currentTime.toFixed(1)}s / {duration.toFixed(1)}s</span>
    </div>
  );
}

API Reference

PCMPlayer Options

interface PCMPlayerOptions {
  sampleRate?: number;       // Sample rate in Hz (default: 16000)
  channels?: number;         // Number of channels (default: 1)
  volume?: number;           // Initial volume 0-1 (default: 1)
  playbackRate?: number;     // Playback speed (default: 1)
  tempo?: number;            // Tempo/speed without pitch change (default: 1)
  pitchSemitones?: number;   // Pitch in semitones -12 to +12 (default: 0)
  pitchCents?: number;       // Fine pitch in cents -1200 to +1200 (default: 0)
  preservePitch?: boolean;   // Preserve pitch when changing speed (default: true)
  loop?: boolean;            // Enable looping (default: false)
  autoPlay?: boolean;        // Auto-play on load (default: false)
  fadeInDuration?: number;   // Fade in duration in seconds (default: 0)
  fadeOutDuration?: number;  // Fade out duration in seconds (default: 0)
  preGain?: number;          // Pre-amplification in dB (default: 0)
}

PCMPlayer Methods

Playback Controls

| Method | Description | |--------|-------------| | play() | Start playback (returns Promise) | | pause() | Pause playback | | stop() | Stop playback and reset position | | toggle() | Toggle play/pause |

Seeking

| Method | Description | |--------|-------------| | seek(time: number) | Seek to specific time in seconds | | seekPercent(percent: number) | Seek to percentage (0-100) | | skipForward(seconds?: number) | Skip forward (default: 10s) | | skipBackward(seconds?: number) | Skip backward (default: 10s) |

Volume

| Method | Description | |--------|-------------| | setVolume(volume: number) | Set volume (0-1) | | getVolume() | Get current volume | | mute() | Mute audio | | unmute() | Unmute audio | | toggleMute() | Toggle mute state | | isMuted() | Check if muted |

Speed & Pitch

| Method | Description | |--------|-------------| | setPlaybackRate(rate: number) | Set playback speed (0.25-4) | | getPlaybackRate() | Get current playback rate | | setPitch(semitones: number) | Set pitch (-12 to +12 semitones) | | getPitch() | Get current pitch |

Loop

| Method | Description | |--------|-------------| | setLoop(loop: boolean) | Enable/disable looping | | isLooping() | Check if looping is enabled | | toggleLoop() | Toggle loop mode |

State

| Method | Description | |--------|-------------| | getState() | Get current player state | | getCurrentTime() | Get current playback time | | getDuration() | Get total duration | | getProgress() | Get playback progress (0-1) | | getBufferInfo() | Get audio buffer information |

Events

| Method | Description | |--------|-------------| | on(event, handler) | Subscribe to an event | | off(event, handler) | Unsubscribe from an event | | once(event, handler) | Subscribe to an event once |

Audio Manipulation

| Method | Description | |--------|-------------| | loadSamples(samples, options?) | Load audio samples | | getWaveformData(options?) | Generate waveform data | | getAnalysisData() | Get real-time audio analysis |

Effects

| Method | Description | |--------|-------------| | setEqualizer(bands) | Set equalizer bands | | setFadeIn(duration) | Set fade in duration | | setFadeOut(duration) | Set fade out duration | | setPreGain(gainDb) | Set pre-amplification |

Export

| Method | Description | |--------|-------------| | exportAudio(options?) | Export as Blob | | downloadAudio(filename?, options?) | Download audio file |

Lifecycle

| Method | Description | |--------|-------------| | destroy() | Clean up resources | | getSnapshot() | Get state snapshot | | restoreSnapshot(snapshot) | Restore from snapshot |

Events

| Event | Payload | Description | |-------|---------|-------------| | stateChange | { state, previousState } | Player state changed | | timeUpdate | { currentTime, duration, progress } | Playback time updated | | volumeChange | { volume, muted } | Volume or mute changed | | playbackRateChange | { playbackRate } | Playback rate changed | | pitchChange | { pitchSemitones } | Pitch changed | | seek | { currentTime, duration } | Seeked to position | | ended | { looped } | Playback ended | | error | { error, message } | Error occurred | | loaded | { duration, sampleRate, channels } | Audio loaded | | play | { currentTime } | Playback started | | pause | { currentTime } | Playback paused | | stop | {} | Playback stopped | | loopChange | { loop } | Loop mode changed |

React Component Props

interface PCMPlayerComponentProps {
  samples: Float32Array | Float32Array[];
  options?: PCMPlayerOptions;
  theme?: PCMPlayerTheme;
  showWaveform?: boolean;          // default: true
  showSpeedControl?: boolean;      // default: true
  showVolumeControl?: boolean;     // default: true
  showTimeDisplay?: boolean;       // default: true
  showDownloadButton?: boolean;    // default: true
  showLoopButton?: boolean;        // default: true
  showPitchControl?: boolean;      // default: false
  className?: string;
  style?: React.CSSProperties;
  width?: string | number;         // default: '100%'
  height?: string | number;        // default: 'auto'
  onReady?: (player) => void;
  onEnded?: () => void;
  onTimeUpdate?: (currentTime, duration) => void;
  onStateChange?: (state) => void;
  onError?: (error) => void;
  title?: string;
  artist?: string;
  coverImage?: string;
  minPlaybackRate?: number;        // default: 0.25
  maxPlaybackRate?: number;        // default: 4
  keyboardShortcuts?: boolean;     // default: true
  ariaLabel?: string;
}

Theme Customization

interface PCMPlayerTheme {
  primaryColor?: string;           // default: '#3b82f6'
  secondaryColor?: string;         // default: '#e5e7eb'
  textColor?: string;              // default: '#1f2937'
  backgroundColor?: string;        // default: '#ffffff'
  progressColor?: string;          // default: '#3b82f6'
  progressBackgroundColor?: string; // default: '#e5e7eb'
  borderRadius?: string;           // default: '8px'
  fontFamily?: string;
  fontSize?: string;               // default: '14px'
  buttonSize?: string;             // default: '40px'
  spacing?: string;                // default: '12px'
  boxShadow?: string;
}

Audio Utilities

import {
  // Conversion
  int16ToFloat32,
  float32ToInt16,
  uint8ToFloat32,
  arrayBufferToFloat32,
  
  // Channel manipulation
  mergeChannels,
  splitChannels,
  interleaveChannels,
  
  // Audio processing
  normalize,
  applyGain,
  fadeIn,
  fadeOut,
  trimSilence,
  resample,
  reverse,
  concat,
  slice,
  mix,
  
  // Analysis
  generateWaveform,
  calculateRMS,
  calculatePeak,
  getDuration,
  
  // Export
  createWavBlob,
  createAudioURL,
  downloadAudio,
  
  // Generation
  generateSilence,
  generateSineWave,
  generateWhiteNoise,
} from 'play-pcm';

Examples

Converting PCM from Different Sources

import { int16ToFloat32, uint8ToFloat32, arrayBufferToFloat32 } from 'play-pcm';

// From Int16Array (common for Web Audio API)
const int16Data = new Int16Array([...]);
const samples = int16ToFloat32(int16Data);

// From raw bytes (e.g., WebSocket)
const rawBytes = new Uint8Array([...]);
const samples = uint8ToFloat32(rawBytes, 2); // 2 bytes per sample (16-bit)

// From ArrayBuffer (e.g., fetch response)
const response = await fetch('audio.pcm');
const buffer = await response.arrayBuffer();
const samples = arrayBufferToFloat32(buffer);

Audio Processing

import { normalize, fadeIn, fadeOut, concat, resample } from 'play-pcm';

// Normalize audio
const normalized = normalize(samples, 0.9); // 90% peak

// Add fade effects
const withFadeIn = fadeIn(samples, 44100); // 1 second fade at 44.1kHz
const withFadeOut = fadeOut(samples, 44100);

// Concatenate audio
const combined = concat(samples1, samples2, samples3);

// Resample to different rate
const resampled = resample(samples, 16000, 44100); // 16kHz to 44.1kHz

Custom Waveform Visualization

import { usePCMPlayer, useAudioVisualization } from 'play-pcm';

function WaveformVisualizer({ samples }: { samples: Float32Array }) {
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const { player } = usePCMPlayer(samples);

  useAudioVisualization(player, canvasRef, {
    type: 'bars', // 'bars' | 'waveform' | 'circle'
    color: '#3b82f6',
    backgroundColor: 'transparent',
    barWidth: 3,
    barGap: 1,
  });

  return <canvas ref={canvasRef} width={300} height={100} />;
}

Dark Theme

<PCMPlayerComponent
  samples={samples}
  theme={{
    primaryColor: '#6366f1',
    secondaryColor: '#374151',
    textColor: '#f9fafb',
    backgroundColor: '#1f2937',
    progressColor: '#6366f1',
    progressBackgroundColor: '#374151',
  }}
/>

Export and Download

const player = createPCMPlayer();
player.loadSamples(samples);

// Export as WAV blob
const wavBlob = await player.exportAudio({
  format: 'wav',
  bitDepth: 16,
  sampleRate: 44100,
  normalize: true,
});

// Export specific portion
const portion = await player.exportAudio({
  startTime: 5,
  endTime: 10,
});

// Direct download
await player.downloadAudio('my-audio.wav', {
  format: 'wav',
  bitDepth: 24,
});

Keyboard Shortcuts

When keyboardShortcuts is enabled (default):

| Key | Action | |-----|--------| | Space | Play/Pause | | ← | Skip back 5 seconds | | → | Skip forward 5 seconds | | ↑ | Volume up 10% | | ↓ | Volume down 10% | | M | Toggle mute | | L | Toggle loop |

Browser Support

  • Chrome 66+
  • Firefox 57+
  • Safari 14.1+
  • Edge 79+

TypeScript Support

Full TypeScript support with comprehensive type definitions. Import types directly:

import type {
  PCMPlayerOptions,
  PCMPlayerInstance,
  PlayerState,
  PlayerEventType,
  WaveformData,
  AudioExportOptions,
  PCMPlayerTheme,
  PCMPlayerComponentProps,
  UsePCMPlayerReturn,
} from 'play-pcm';

License

MIT © 2026

Contributing

Contributions are welcome! Please read our contributing guidelines before submitting a PR.

Changelog

See CHANGELOG.md for release history.