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')