PWGL Extensions
A comprehensive utility package extending the capabilities of PWGL (Programmable WebGL) with advanced controls, audio processing, rendering enhancements, and utility functions for game development and interactive graphics applications.
Overview
The PWGL Extensions library provides a collection of modules designed to streamline common development patterns, from input handling and audio management to collision detection and mathematical utilities. All functionality is exposed through the global PWGLExtensions (alias AGLExtensions) namespace.
Extension source files live under extensions/src. The full bundle starts from extensions/src/index.js, while the standalone group bundles start from:
extensions/src/utils/entry.jsextensions/src/audio/entry.jsextensions/src/controls/entry.jsextensions/src/display/entry.js
Each group entry creates PWGLExtensions if needed and then merges its own group namespace, so the UMD files can be loaded independently or together.
Modules
Controls
Comprehensive input handling for keyboard, mouse, and gamepad devices.
PressState (src/controls/PressState.js)
Base class for tracking pressed and released states of input devices.
- Tracks down/up/pressed state and press duration for input events
- Provides
isDown(),isUp(),isPressed(),isLongPressed(), andgetDuration() - Ignores repeated down events while a key/button is already held
- Foundation for all input handler implementations
Keyboard (src/controls/Keyboard.js)
Handles keyboard input events and key state tracking.
- Extends
PressStatefor consistent state management - Tracks pressed keys in real-time
- Optional target element for scoped event handling
- Methods:
destruct()- Clean up event listeners
Mouse (src/controls/Mouse.js)
Tracks primary mouse/touch position and press state.
- Position tracking (
x,ycoordinates) - Primary pointer state management through
PressState - Supports mouse and touch events
- Supports both document and element-scoped tracking
Gamepad (src/controls/Gamepad.js)
Full gamepad/controller input support using the Gamepad API.
- Normalized button and axis snapshots
- Multi-gamepad support
get(id)returns a normalized gamepad ornullanyreturns the first connected normalized gamepad ornullisAnyConnected()checks if at least one gamepad is available
Audio
Professional audio manipulation and mixing utilities powered by the Web Audio API.
BaseAudio (src/audio/BaseAudio.js)
Abstract base class for audio node management and control.
- Properties:
volume- Audio gain control (0-1 range)muted- Mutes output without changing the stored volumepan- Stereo panning (-1 to 1)reverb- Reverb effect parameterfilters- Ordered audio filter list
- Features: Manages Web Audio API node connections, audio routing, and effect chains
Audio Filters (src/audio/filters/)
Ordered Web Audio filter classes that can be attached to AudioItem or AudioMixer instances.
BaseAudioFilter- Base input/output interface for custom audio filtersBiquadAudioFilter- Base class forBiquadFilterNodefiltersLowPassAudioFilter- Low-pass filterHighPassAudioFilter- High-pass filterBandPassAudioFilter- Band-pass filterNotchAudioFilter- Notch filterPeakingAudioFilter- Boosts or cuts a focused frequency bandLowShelfAudioFilter- Boosts or cuts frequencies below a cutoffHighShelfAudioFilter- Boosts or cuts frequencies above a cutoff- Filters run in array order and can be bypassed with
filter.on = false - Built-in numeric parameters are normalized before they are written to Web Audio params
AudioItem (src/audio/AudioItem.js)
Single audio playback with full playback control.
- Play, pause, and stop controls
- Looping support
- Seek, duration, and current time tracking
- Volume and panning envelope automation
AudioMixer (src/audio/AudioMixer.js)
Multi-channel audio mixer for complex audio management.
- Mix multiple audio tracks simultaneously
- Per-track volume and pan control
- Master volume control
- Effect chain management
Audio Utilities (src/audio/utils/)
Helper functions for audio manipulation.
fadeAudioVolume(audioItem, min, max, step)- Set a mixed volume valuecrossFadeAudioVolumes(audioItemA, audioItemB, min, max, step)- Crossfade between two items
Migration: Audio Filters
The fixed low-pass and high-pass BaseAudio properties were removed in the major filter API update.
Replace old direct filter properties:
audio.lowPassFilterFrequency = 6000;
audio.lowPassFilterQ = 1;
audio.highPassFilterFrequency = 120;
audio.highPassFilterQ = 0.7;
with explicit filter instances:
audio.filters = [
new PWGLExtensions.audio.HighPassAudioFilter({ frequency: 120, Q: 0.7 }),
new PWGLExtensions.audio.LowPassAudioFilter({ frequency: 6000, Q: 1 }),
];
Use the same filters array on AudioMixer for master output filtering.
Display
Specialized rendering components for visual effects.
AnimatedWater (src/display/AnimatedWater.js)
Realistic animated water surface effect using displacement mapping.
- Layered water displacement simulation
- Configurable animation speed and distortion level
- Scale and offset control
- Uses noise textures for natural water behavior
- Optimized for real-time rendering
SmoothLight (src/display/SmoothLight.js)
Smooth lighting and shadow calculations for dynamic scenes.
- Soft shadow rendering
- Light falloff and attenuation
- Optimized lighting calculations
- Suitable for deferred and forward rendering
Utilities
A comprehensive collection of mathematical, collision detection, and utility functions.
Mathematical Utilities
clamp(min, max, value)- Constrain value within rangemix(a, b, t)- Linear interpolation between valuesfract(value)- Fractional part of a number (same asfract()in GLSL)dot(a, b)- Dot product of two vectorscross(a, b)- Cross product of two vectorsenumCheck(value, enumObj)- Validate enum values
Collision Detection (collisionDetection)
Advanced spatial collision algorithms for game development:
areTwoRectsCollided(rect1, rect2)- AABB rectangle collisionareTwoLinesCollided(line1, line2)- Line segment intersectionrectToRectIntersection(rect1, rect2)- Get intersection rectanglelineToLineIntersection(line1, line2)- Find intersection pointdistanceBetweenPointAndLine(point, line)- Point-to-line distance calculation
Rectangles use { x, y, width, height }, where width and height are sizes.
Array and Object Utilities
areObjectsEqual(obj1, obj2)- Deep equality comparisonarraySet(target, source, offset)- Copy source values into a target array from an optional offsetdeepFreeze(value)- Recursively freeze a plain object or arrayremoveFromArray(array, item)- Remove first occurrence from array
Procedural Generation
generateDungeon(iterations, sampleRooms)- Procedural dungeon layout generationhashNoise2D(x, y)- 2D hash noise function for terrain generationstepNoise(value, steps)- Quantized noise for stylized effectsgetRandomFrom(array)- Get random element from array
Animation and Timing
nthCall(fn, n)- Execute function every nth callenterFrame(callback, fps)- Frame-based animation loopFPSCounterclass /getFPS()- Frame rate monitoring and statistics
Grid and Spatial Mapping
coordToVector(x, y, width)- Convert 2D grid coordinates to linear indexvectorToCoord(index, width)- Convert linear index to 2D coordinates
State Management
createStateMachine(config)- Small observable state container- Accepts
initialStateand action functions on the same config object - Batches subscriber notifications into the next microtask after actions run
- Skips notification when an action explicitly returns
false - Deeply freezes subscriber snapshots by default
- Supports
strict: falseto skip freezing in performance-critical code - Passes the previous snapshot as the second subscriber argument; it is
undefinedonly during the initial subscription
- Accepts
With strict: false, snapshots are still cloned but are mutable. Subscribers must not modify them: all listeners in the same notification receive the same snapshot, and that object becomes the next previousState.
Utility Functions
noop()- No-operation function (often used as default callback)noopReturnsWith(value)- Returns a function that returns the specified value
Usage
All extensions are accessed through the global namespace:
// Input handling
const keyboard = new PWGLExtensions.controls.Keyboard();
const mouse = new PWGLExtensions.controls.Mouse();
const gamepad = new PWGLExtensions.controls.Gamepad();
const firstPad = gamepad.any;
// Audio
const audio = new PWGLExtensions.audio.AudioItem(audioUrl);
const mixer = new PWGLExtensions.audio.AudioMixer();
audio.filters = [
new PWGLExtensions.audio.HighPassAudioFilter({ frequency: 120 }),
new PWGLExtensions.audio.NotchAudioFilter({ frequency: 440, Q: 8 }),
new PWGLExtensions.audio.LowPassAudioFilter({ frequency: 6000 }),
];
class TelephoneAudioFilter extends PWGLExtensions.audio.BaseAudioFilter {
createNodes(context) {
this.input = this.output = context.createBiquadFilter();
this.input.type = "bandpass";
this.input.frequency.value = 1400;
this.input.Q.value = 0.8;
}
}
audio.filters.push(new TelephoneAudioFilter());
audio.filters[0].on = false;
// Visual effects
const water = new PWGLExtensions.display.AnimatedWater(noiseTexture, speed);
// Utilities
const distance = PWGLExtensions.utils.clamp(0, 100, value);
const collides = PWGLExtensions.utils.collisionDetection.areTwoRectsCollided(rect1, rect2);
const counter = PWGLExtensions.utils.createStateMachine({
initialState: { count: 0 },
strict: true,
increment(state) {
state.count += 1;
},
setCount(state, count) {
if (state.count === count) return false;
state.count = count;
},
});
counter.subscribe((state, prevState) => {
console.log(state.count, prevState?.count);
});
counter.increment();
Installation
The extensions package is included with PWGL. Load the all-in-one bundle when you need every group:
<script src="pwgl.extensions.umd.js" type="text/javascript"></script>
Or load only the standalone group bundles you need:
<script src="pwgl.extensions.utils.umd.js" type="text/javascript"></script>
<script src="pwgl.extensions.controls.umd.js" type="text/javascript"></script>
<script src="pwgl.extensions.audio.umd.js" type="text/javascript"></script>
<script src="pwgl.extensions.display.umd.js" type="text/javascript"></script>
The same outputs are available as ES modules:
import * as PWGLExtensions from "./dist/pwgl.extensions.es.js";
import * as PWGLControls from "./dist/pwgl.extensions.controls.es.js";
Browser Compatibility
- Requires ECMAScript 2015 (ES6) or later
- Some utilities use
structuredClonefor deep copies - Audio features require Web Audio API support
- Gamepad support requires the Gamepad API
- Graphics features require WebGL context
Performance Considerations
- Use
areObjectsEqual()andcreateStateMachine()judiciously for large datasets - Collision detection functions are optimized for AABB and simple geometry
- Audio processing is GPU-accelerated through Web Audio API
- Frame-rate independent timing recommended for animation loops
License
See LICENSE file in the repository root for licensing information.