Skip to content

EZ Web Audio / GrainPlayer

Class: GrainPlayer ​

Defined in: packages/core/src/grain-player.ts:74

Granular synthesis player that generates continuous texture/pad sounds from an audio buffer.

GrainPlayer works by scheduling many overlapping short "grains" of audio from a source buffer. Each grain gets a triangular window envelope (linear ramp up -> linear ramp down) to prevent clicks. Grains are scheduled slightly ahead of real time using a WorkerTimer for glitch-free playback even in background tabs.

Provides independent control over:

  • Position (0-1): Which region of the buffer to sample grains from
  • Pitch (semitones): Pitch shift via playbackRate on each grain's BufferSourceNode
  • Grain parameters: grainSize, overlap, jitter — adjustable in real-time

Note: Pitch shifting is implemented via playbackRate, which changes both pitch AND speed of each grain. The overlap system compensates for the changed grain duration, but extreme values (beyond +/-24 semitones) may affect texture quality.

Example ​

typescript
import { createGrainPlayer, createSound } from 'ez-web-audio'

const sound = await createSound('pad.mp3')
const grains = await createGrainPlayer(sound.audioBuffer, {
  grainSize: 0.1,
  overlap: 0.05,
  jitter: 0.1
})

grains.play()
grains.position = 0.5  // scrub to middle of buffer
grains.pitch = 7       // pitch up a fifth

Extends ​

Constructors ​

Constructor ​

new GrainPlayer(audioContext, buffer, options?): GrainPlayer

Defined in: packages/core/src/grain-player.ts:110

Parameters ​

audioContext ​

AudioContext

buffer ​

AudioBuffer

options? ​

GrainPlayerOptions

Returns ​

GrainPlayer

Overrides ​

TypedEventEmitter.constructor

Properties ​

audioContext ​

readonly audioContext: AudioContext

Defined in: packages/core/src/grain-player.ts:111

Accessors ​

activeGrainCount ​

Get Signature ​

get activeGrainCount(): number

Defined in: packages/core/src/grain-player.ts:404

Number of currently active (playing) grains.

Returns ​

number


disposed ​

Get Signature ​

get disposed(): boolean

Defined in: packages/core/src/grain-player.ts:401

Whether this GrainPlayer has been disposed.

Returns ​

boolean


grainSize ​

Get Signature ​

get grainSize(): number

Defined in: packages/core/src/grain-player.ts:453

Duration of each grain in seconds. Minimum 0.01s. Changes take effect on the next grain. Note: Shrinking grainSize will re-clamp overlap to maintain valid hop size.

Returns ​

number

Set Signature ​

set grainSize(value): void

Defined in: packages/core/src/grain-player.ts:454

Parameters ​
value ​

number

Returns ​

void


jitter ​

Get Signature ​

get jitter(): number

Defined in: packages/core/src/grain-player.ts:474

Random scatter around the position for organic texture, 0-1. 0 = no scatter, 1 = scatter across entire buffer.

Returns ​

number

Set Signature ​

set jitter(value): void

Defined in: packages/core/src/grain-player.ts:475

Parameters ​
value ​

number

Returns ​

void


loop ​

Get Signature ​

get loop(): boolean

Defined in: packages/core/src/grain-player.ts:480

Whether to loop when reaching the buffer end.

Returns ​

boolean

Set Signature ​

set loop(value): void

Defined in: packages/core/src/grain-player.ts:481

Parameters ​
value ​

boolean

Returns ​

void


overlap ​

Get Signature ​

get overlap(): number

Defined in: packages/core/src/grain-player.ts:465

Overlap between consecutive grains in seconds. Clamped to [0, grainSize - 0.001] to ensure hop size never drops below 0.001s. Changes take effect on the next grain.

Returns ​

number

Set Signature ​

set overlap(value): void

Defined in: packages/core/src/grain-player.ts:466

Parameters ​
value ​

number

Returns ​

void


paused ​

Get Signature ​

get paused(): boolean

Defined in: packages/core/src/grain-player.ts:398

Whether the grain player is paused.

Returns ​

boolean


pitch ​

Get Signature ​

get pitch(): number

Defined in: packages/core/src/grain-player.ts:431

Pitch shift in semitones. 0 = original pitch. Internally converts to playbackRate via 2^(semitones/12).

Note: This is playbackRate-based pitch shift. It changes grain duration, which the overlap system compensates for, but extreme values will affect texture quality.

Example ​
typescript
grainPlayer.pitch = 7   // up a fifth
grainPlayer.pitch = 12  // up an octave
grainPlayer.pitch = -12 // down an octave
Returns ​

number

Set Signature ​

set pitch(semitones): void

Defined in: packages/core/src/grain-player.ts:432

Parameters ​
semitones ​

number

Returns ​

void


playbackRate ​

Get Signature ​

get playbackRate(): number

Defined in: packages/core/src/grain-player.ts:441

Direct playback rate ratio. 1 = original speed, 2 = double speed (octave up). Setting this also updates the pitch property accordingly.

Returns ​

number

Set Signature ​

set playbackRate(value): void

Defined in: packages/core/src/grain-player.ts:442

Parameters ​
value ​

number

Returns ​

void


playing ​

Get Signature ​

get playing(): boolean

Defined in: packages/core/src/grain-player.ts:395

Whether the grain player is currently playing.

Returns ​

boolean


position ​

Get Signature ​

get position(): number

Defined in: packages/core/src/grain-player.ts:411

Playback position in the buffer, normalized 0-1. 0 = beginning of buffer, 1 = end of buffer. Changes take effect on the next grain.

Returns ​

number

Set Signature ​

set position(value): void

Defined in: packages/core/src/grain-player.ts:412

Parameters ​
value ​

number

Returns ​

void

Methods ​

_clearListeners() ​

protected _clearListeners(): void

Defined in: packages/core/src/events/typed-event-emitter.ts:192

Remove every listener registered through this emitter (via addEventListener(), on(), or once()), regardless of how many or what event type they're bound to.

Native EventTarget has no removeAllListeners(). This works around that by aborting a shared AbortSignal threaded through every addEventListener() call this class makes, then swapping in a fresh AbortController so the instance can keep accepting new listeners afterward (e.g. if it's reused before being garbage collected).

Subclasses call this from their dispose() alongside neutering dispatchEvent — the neuter stops future emits, this stops stale listener closures from being retained/invoked at all.

Returns ​

void

Inherited from ​

TypedEventEmitter._clearListeners


addEffect() ​

addEffect(effect, position?): this

Defined in: packages/core/src/grain-player.ts:669

Add an effect to the shared output bus. All grains are affected.

Parameters ​

effect ​

Effect

The Effect instance to add

position? ​

number

Optional index to insert at

Returns ​

this

this for chaining


addEventListener() ​

Call Signature ​

addEventListener<K>(type, listener, options?): void

Defined in: packages/core/src/events/typed-event-emitter.ts:44

Add a typed event listener for known event types. Overloaded to provide type safety for known event types while remaining compatible with the native EventTarget API.

Type Parameters ​
K ​

K extends "pause" | "play" | "dispose" | "stop" | "resume"

Parameters ​
type ​

K

The event type (key of TMap)

listener ​

(event) => void

Typed event handler

options? ​

Standard addEventListener options

boolean | AddEventListenerOptions

Returns ​

void

Inherited from ​

TypedEventEmitter.addEventListener

Call Signature ​

addEventListener(type, listener, options?): void

Defined in: packages/core/src/events/typed-event-emitter.ts:49

Add a typed event listener for known event types. Overloaded to provide type safety for known event types while remaining compatible with the native EventTarget API.

Parameters ​
type ​

string

The event type (key of TMap)

listener ​

Typed event handler

EventListenerOrEventListenerObject | null

options? ​

Standard addEventListener options

boolean | AddEventListenerOptions

Returns ​

void

Inherited from ​

TypedEventEmitter.addEventListener


changeGainTo() ​

changeGainTo(value): this

Defined in: packages/core/src/grain-player.ts:644

Set the master gain for all grains.

Parameters ​

value ​

number

Gain from 0 (silent) to 1 (full volume)

Returns ​

this


changePanTo() ​

changePanTo(value): this

Defined in: packages/core/src/grain-player.ts:654

Set the master pan for all grains.

Parameters ​

value ​

number

Pan from -1 (left) to 1 (right)

Returns ​

this


dispose() ​

dispose(): void

Defined in: packages/core/src/grain-player.ts:741

Dispose this GrainPlayer, releasing all audio resources.

Stops playback, disconnects the shared bus, and marks the instance as unusable. Dispose is idempotent.

Returns ​

void


emit() ​

protected emit<K>(type, detail): void

Defined in: packages/core/src/events/typed-event-emitter.ts:95

Emit a typed event with the given detail.

Creates a CustomEvent with the provided detail and dispatches it on this target. Subclasses call this internally to fire lifecycle events.

Type Parameters ​

K ​

K extends "pause" | "play" | "dispose" | "stop" | "resume"

Parameters ​

type ​

K

The event type to emit (key of TMap)

detail ​

GrainPlayerEventMap[K]["detail"]

The event detail object (typed by TMap)

Returns ​

void

Inherited from ​

TypedEventEmitter.emit


getAnalyzer() ​

getAnalyzer(): Analyzer | null

Defined in: packages/core/src/grain-player.ts:717

Get the currently attached analyzer.

Returns ​

Analyzer | null


getEffects() ​

getEffects(): readonly Effect[]

Defined in: packages/core/src/grain-player.ts:698

Get a readonly copy of the current effects array.

Returns ​

readonly Effect[]


getGainNode() ​

getGainNode(): GainNode

Defined in: packages/core/src/grain-player.ts:488

Returns the master GainNode (for LFO targeting and external routing).

Returns ​

GainNode


getPannerNode() ​

getPannerNode(): StereoPannerNode

Defined in: packages/core/src/grain-player.ts:491

Returns the master StereoPannerNode (for LFO targeting and external routing).

Returns ​

StereoPannerNode


off() ​

off<K>(type, listener): this

Defined in: packages/core/src/events/typed-event-emitter.ts:167

Unsubscribe from an event.

Note: Due to native EventTarget limitations, you must provide the same listener function reference that was used when subscribing.

Type Parameters ​

K ​

K extends "pause" | "play" | "dispose" | "stop" | "resume"

Parameters ​

type ​

K

The event type to unsubscribe from

listener ​

(event) => void

The event handler function to remove

Returns ​

this

this for chaining

Example ​

typescript
const handler = (e) => console.log(e.detail)
emitter.on('play', handler)
// later...
emitter.off('play', handler)

Inherited from ​

TypedEventEmitter.off


on() ​

on<K>(type, listener): this

Defined in: packages/core/src/events/typed-event-emitter.ts:116

Subscribe to one or more events. Supports chaining.

Type Parameters ​

K ​

K extends "pause" | "play" | "dispose" | "stop" | "resume"

Parameters ​

type ​

The event type(s) to subscribe to (key or array of keys of TMap)

K | K[]

listener ​

(event) => void

The event handler function

Returns ​

this

this for chaining

Example ​

typescript
emitter.on('play', handlePlay).on('stop', handleStop)
emitter.on(['play', 'stop'], handleBoth)

Inherited from ​

TypedEventEmitter.on


once() ​

once<K>(type, listener): this

Defined in: packages/core/src/events/typed-event-emitter.ts:141

Subscribe to an event once. Handler is removed after first invocation.

Type Parameters ​

K ​

K extends "pause" | "play" | "dispose" | "stop" | "resume"

Parameters ​

type ​

K

The event type to subscribe to

listener ​

(event) => void

The event handler function

Returns ​

this

this for chaining

Example ​

typescript
emitter.once('end', () => console.log('Finished'))

Inherited from ​

TypedEventEmitter.once


onPlayRamp() ​

onPlayRamp(type, rampType?): object

Defined in: packages/core/src/grain-player.ts:581

Schedule a master gain/pan ramp when play() is called.

Same fluent shape as BaseSound.onPlayRamp() (R1#6). Consume-once: the ramp is applied and cleared by the next play() call.

Parameters ​

type ​

'gain' or 'pan'

"gain" | "pan"

rampType? ​

RampType

'linear' or 'exponential' (default: 'exponential')

Returns ​

object

Fluent builder for setting start value, end value, and duration

from() ​

from: (startValue) => object

Parameters ​
startValue ​

number

Returns ​

object

to() ​

to: (endValue) => object

Parameters ​
endValue ​

number

Returns ​

object

in() ​

in: (endTime) => void

Parameters ​
endTime ​

number

Returns ​

void

Example ​

typescript
grainPlayer.onPlayRamp('gain', 'linear').from(0).to(0.8).in(2)
grainPlayer.play()

onPlaySet() ​

onPlaySet(type): object

Defined in: packages/core/src/grain-player.ts:540

Schedule a master gain/pan value to be set when play() is called.

Same fluent shape as BaseSound.onPlaySet() (R1#6) — use .at(time) for an immediate setValueAtTime, or .endingAt(time, rampType) to ramp to the value. Consume-once: the schedule is applied and cleared by the next play() call.

Parameters ​

type ​

'gain' or 'pan'

"gain" | "pan"

Returns ​

object

Fluent builder for setting value and timing

to() ​

to: (value) => object

Parameters ​
value ​

number

Returns ​

object

at() ​

at: (time) => void

Parameters ​
time ​

number

Returns ​

void

endingAt() ​

endingAt: (time, rampType?) => void

Parameters ​
time ​

number

rampType? ​

RampType

Returns ​

void

Example ​

typescript
// Fade the texture in over 2 seconds
grainPlayer.onPlaySet('gain').to(0).at(0)
grainPlayer.onPlaySet('gain').to(0.8).endingAt(2, 'linear')
grainPlayer.play()

pause() ​

pause(): void

Defined in: packages/core/src/grain-player.ts:355

Pause grain playback. Active grains will finish naturally. Resume with resume.

Returns ​

void

Example ​

typescript
grainPlayer.pause()
// later...
grainPlayer.resume()

play() ​

play(): void

Defined in: packages/core/src/grain-player.ts:288

Start grain playback. If already playing, this is a no-op.

Returns ​

void

Example ​

typescript
grainPlayer.play()

removeEffect() ​

removeEffect(effect): this

Defined in: packages/core/src/grain-player.ts:686

Remove an effect from the shared output bus.

Parameters ​

effect ​

Effect

The Effect instance to remove

Returns ​

this

this for chaining


removeEventListener() ​

Call Signature ​

removeEventListener<K>(type, listener, options?): void

Defined in: packages/core/src/events/typed-event-emitter.ts:70

Remove a typed event listener for known event types. Overloaded to provide type safety for known event types while remaining compatible with the native EventTarget API.

Type Parameters ​
K ​

K extends "pause" | "play" | "dispose" | "stop" | "resume"

Parameters ​
type ​

K

The event type (key of TMap)

listener ​

(event) => void

Typed event handler to remove

options? ​

Standard removeEventListener options

boolean | EventListenerOptions

Returns ​

void

Inherited from ​

TypedEventEmitter.removeEventListener

Call Signature ​

removeEventListener(type, listener, options?): void

Defined in: packages/core/src/events/typed-event-emitter.ts:75

Remove a typed event listener for known event types. Overloaded to provide type safety for known event types while remaining compatible with the native EventTarget API.

Parameters ​
type ​

string

The event type (key of TMap)

listener ​

Typed event handler to remove

EventListenerOrEventListenerObject | null

options? ​

Standard removeEventListener options

boolean | EventListenerOptions

Returns ​

void

Inherited from ​

TypedEventEmitter.removeEventListener


resume() ​

resume(): void

Defined in: packages/core/src/grain-player.ts:377

Resume grain playback from where it was paused.

Returns ​

void

Example ​

typescript
grainPlayer.resume()

setAnalyzer() ​

setAnalyzer(analyzer): this

Defined in: packages/core/src/grain-player.ts:708

Attach an analyzer to the shared bus output for visualization.

Parameters ​

analyzer ​

The Analyzer instance, or null to detach

Analyzer | null

Returns ​

this

this for chaining


setDestination() ​

setDestination(node): this

Defined in: packages/core/src/grain-player.ts:727

Set a custom destination for audio output.

Parameters ​

node ​

AudioNode

The AudioNode to route output to

Returns ​

this

this for chaining


stop() ​

stop(): void

Defined in: packages/core/src/grain-player.ts:329

Stop grain playback. Active grains will decay naturally (they're very short).

Returns ​

void

Example ​

typescript
grainPlayer.stop()

update() ​

update(type): object

Defined in: packages/core/src/grain-player.ts:506

Update a master bus parameter immediately.

Parameters ​

type ​

'gain' or 'pan'

"gain" | "pan"

Returns ​

object

Fluent builder

to() ​

to: (value) => object

Parameters ​
value ​

number

Returns ​

object

as() ​

as: (method) => void

Parameters ​
method ​

RatioType

Returns ​

void

Example ​

typescript
grainPlayer.update('gain').to(0.5).as('ratio')