A production-ready WebGPU image processing library for sequential filter processing with multi-pass support.
SequentialGPU provides a powerful, flexible framework for GPU-accelerated image processing using the WebGPU API. Built for production use with extensive performance optimizations and comprehensive monitoring capabilities.
- Sequential Filter Processing - Apply multiple filters in sequence with multi-pass support
- High Performance - 0.4ms render times with advanced GPU optimizations
- Multiple Shader Types - Support for both fragment and compute shaders
- Dynamic Buffer Management - Easy updating of filter parameters at runtime
- Flexible Texture Handling - Create and manage multiple input/output textures
- MSAA Support - Multi-sample anti-aliasing for high-quality rendering
- Unified Statistics System - Real-time performance monitoring and analytics
- Advanced Caching - Pipeline caching (>99% hit rate), bind group caching (85-90% hit rate)
- Memory Management - Automatic buffer pooling, texture pooling, and resource cleanup
- Queue System - Priority-based operation queue with async GPU operations
- Modern browser with WebGPU support (Chrome 113+, Edge 113+, or Firefox with flags)
- For development: Node.js 18+ and npm
WebGPU Browser Support:
- Chrome/Edge 113+ (stable support)
- Firefox: Enable
dom.webgpu.enabledflag - Safari: Experimental support in Safari Technology Preview
- Installation
- Quick Start
- Usage
- API Reference
- Performance Monitoring
- Render Queue System
- Caching Control
- Advanced Features
- Contributing
- License
To install the package, use npm:
npm install sequentialgpuimport SequentialGPU from 'sequentialgpu';
// Minimal configuration
const settings = {
images: ['path/to/image.png'],
presentationFormat: 'rgba8unorm',
textures: {
outputTexture: { label: 'Processed Output' }
},
filters: {
myFilter: {
active: true,
type: 'render', // 'render' or 'compute'
passes: [{
active: true,
inputTexture: ['texture'],
outputTexture: undefined, // undefined = render to screen
shaderURL: 'shaders/myshader.wgsl'
}]
}
}
};
// Initialize and render
const app = await SequentialGPU.createApp(settings);
await app.loadImage(0);
await app.renderFilterPasses(settings.filters.myFilter);The SequentialGPU module provides a WebGpuRenderer class that initializes WebGPU processing with comprehensive settings:
import SequentialGPU from 'sequentialgpu';
const settings = {
// Image Configuration
imageArray: ['path/to/image1.png', 'path/to/image2.png'], // or 'images' (both supported)
presentationFormat: 'rgba8unorm', // or 'rgba16float'
// Performance Options (Optional)
enableCaching: true, // Enable all caching (default: true)
enableStats: false, // Enable statistics tracking (default: false for performance)
// Texture Definitions
textures: {
textureOneIN_OUT: {
label: 'Processing Buffer 1',
notes: 'Used for intermediate processing',
format: 'rgba8unorm', // Optional: defaults to presentationFormat
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.RENDER_ATTACHMENT,
sampleCount: 1, // 1 or 4 for MSAA
},
textureTwoOUT_IN: {
label: 'Processing Buffer 2'
}
},
// Filter Pipeline
filters: {
firstFilterName: {
label: 'First Filter Name',
active: true,
type: 'render', // 'render' for fragment shaders, 'compute' for compute shaders
passes: [
{
label: 'Filter 1 Pass 1',
active: true,
inputTexture: ['texture'], // 'texture' is auto-created for your loaded image
outputTexture: 'textureOneIN_OUT',
shaderURL: 'path/to/shader.wgsl'
}
],
bufferAttachment: {
groupIndex: 0,
bindingIndex: 3, // Bindings 0-2 reserved; custom bindings start at 3
bindings: {
// Uniform buffers (for fragment/render shaders)
uniformKey: {
type: 'uniform',
value: 255,
},
floatKey: {
type: 'float',
value: [0.0, 0.0, 0.0, 0.0],
}
}
}
},
secondFilterName: {
label: 'Second Filter Name (Compute Shader)',
active: true,
type: 'compute',
passes: [
{
label: 'Filter 2 Pass 1',
active: true,
inputTexture: ['textureOneIN_OUT'],
shaderURL: 'path/to/compute.wgsl'
}
],
bufferAttachment: {
groupIndex: 0,
bindingIndex: 3,
bindings: {
// Storage buffers (for compute shaders)
dataBuffer: {
type: 'storage',
usage: 'readwrite', // 'read', 'write', or 'readwrite'
size: 1024, // Required for storage buffers
value: new Float32Array(256),
}
}
}
},
thirdFilterName: {
label: 'Third Filter Name (Multi-Pass to Screen)',
active: true,
type: 'render',
passes: [
{
label: 'Filter 3 Pass 1',
active: true,
inputTexture: ['textureOneIN_OUT'],
outputTexture: 'textureTwoOUT_IN',
shaderURL: 'path/to/shader.wgsl'
},
{
label: 'Filter 3 Pass 2',
inputTexture: ['textureTwoOUT_IN'],
outputTexture: 'textureTwoOUT_IN', // Reuse same texture (auto-swaps with temp)
shaderURL: 'path/to/shader.wgsl'
},
{
label: 'Filter 3 Pass 3',
inputTexture: ['textureTwoOUT_IN'],
outputTexture: undefined, // undefined = render to screen
shaderURL: 'path/to/shader.wgsl'
}
],
bufferAttachment: {
groupIndex: 0,
bindingIndex: 3,
bindings: {
uniformKey: {
type: 'uniform',
value: 255,
},
floatKey: {
type: 'float',
value: 0.0,
}
}
}
},
}
};
// Initialize the app
const app = await SequentialGPU.createApp(settings).catch(error => {
console.error("Error creating app:", error);
});-
Reserved Texture Name:
textureis automatically created for your loaded image. Use it as the input for your first filter but do not create or define it in your settings. -
System Textures: The following are auto-created and managed:
texture- Main input texture for loaded imagestextureMASS- MSAA texture (4x sampling) for anti-aliasingtextureTemp- Temporary buffer for ping-pong rendering
-
Shader Files:
shaderURLshould point to your WGSL shader files (.wgsl format). -
Buffer Bindings:
- Group 0, Binding 0: Reserved for sampler
- Group 0, Binding 1: Reserved for first input texture
- Group 0, Binding 2: Reserved for second input texture
- Group 0, Binding 3+: Custom buffer attachments (your uniforms/storage)
-
Compute Shaders:
- Set
type: 'compute' - Use storage buffers with
type: 'storage'and specifysize - Buffer usage:
'read','write', or'readwrite'
- Set
-
Render to Screen: Set
outputTexture: undefinedto render the pass output to the screen. -
Texture Reuse: You can use the same texture as input and output. The system automatically swaps with
textureTempto prevent conflicts.
SequentialGPU.createApp(settings)
- Creates and initializes a new WebGPU renderer instance
- Parameters: Configuration object (see Usage)
- Returns: Promise
- Recommended: Use this factory method instead of direct instantiation
const app = await SequentialGPU.createApp(settings);// Load a specific image from the settings.images array
await app.loadImage(imageIndex);
// Resize canvas and recreate resources
await app.resize(width, height, resetSize);// Update buffer values dynamically
app.updateFilterBuffer('intensity', 1.5);
app.updateFilterBuffer('colorMatrix', new Float32Array([1.0, 0.0, 0.0, 1.0]));
// Update input textures for a filter pass
app.updateFilterInputTexture(
'filterName', // Filter key
0, // Pass index
1, // Binding index
'newTexture', // New texture key
0 // Texture index
);
// Render specific filter and its passes
const isScreenRender = await app.renderFilterPasses(settings.filters.myFilter);
if (isScreenRender) {
console.log('Rendered to screen');
}
// Wait for GPU to complete all operations
await app.waitForRenderComplete();// Clean up all GPU resources
await app.dispose();Statistics tracking is disabled by default to maximize performance. Enable it for debugging or profiling:
const settings = {
images: ['path/to/image.png'],
enableStats: true, // Enable statistics tracking
// ... rest of settings
};
const app = await SequentialGPU.createApp(settings);// Get complete statistics object
const stats = app.getStats();
// Get specific category
const renderStats = app.getStats({ category: 'render' });
// Human-readable format
const formatted = app.getFormattedStats();
console.log('Resolution:', formatted.render.canvas.resolution);
console.log('FPS:', formatted.render.fps);
console.log('Frame Time:', formatted.render.avgFrameTime);
console.log('Memory:', formatted.memory.current);
console.log('Cache Hit Rate:', formatted.pipelines.cacheHitRate);
// High-level performance overview
const summary = app.getPerformanceSummary();
console.log(`FPS: ${summary.fps}`);
console.log(`Frame Time: ${summary.frameTimeMs}ms`);
console.log(`Canvas: ${summary.canvas.resolution}`);
// Export complete stats for external analytics
const exportData = app.exportStats();| Category | Key Metrics |
|---|---|
| Buffers | totalCreated, currentActive, totalMemoryBytes, poolHits/Misses |
| Textures | totalCreated, currentActive, poolEfficiency |
| Pipelines | cacheHits/Misses, cacheHitRate (>99% expected), averageCompilationTimeMs |
| Bind Groups | cacheHits/Misses, hitRate (85-90% expected), forcedRecreations |
| Queue | totalOperations, completedOperations, averageExecutionTimeMs |
| Render | totalFrames, framesPerSecond, averageFrameTimeMs, canvas/original dimensions |
| Throughput | canvas.megapixelsPerSecond, original.megapixelsPerSecond |
| Memory | currentUsageBytes, peakUsageBytes, pressureLevel |
For complete statistics documentation including all fields, historical data, and multi-resolution tracking, see Stats API Quick Start and Stats API Guide.
SequentialGPU includes a built-in render queue for managing GPU operations with priority-based scheduling:
// Queue operations with priority control
// Operation: Function that returns a Promise
// Priority: 'urgent', 'high', 'normal', 'low', 'background'
// Metadata: Object for tagging operations
await app.queueOperation(async () => {
return await someAsyncOperation();
}, 'high', {
type: 'custom',
operation: 'myOperation',
description: 'Custom processing task'
});
// Queue management
const status = app.getRenderQueueStatus();
const stats = app.getQueuePerformanceStats();
app.cancelRenderOperations('filterType');
app.clearRenderQueue();Priority Levels: 'urgent' > 'high' > 'normal' (default) > 'low' > 'background'
For comprehensive queue statistics, use the unified stats system (app.getStats()) instead of legacy queue-specific methods.
SequentialGPU includes comprehensive caching for optimal performance (>99% cache hit rates). For debugging or testing, you can disable all caching:
const settings = {
enableCaching: false, // Disable all caching (default: true)
// ... rest of settings
};When enableCaching: false:
- Shaders compile fresh every time
- Pipelines are created without caching
- Texture pooling is disabled
- Performance will be significantly impacted
Use cases: Debugging shader compilation, testing dynamic shader modifications, performance profiling.
Production use: Always keep caching enabled (default).
For details, see Caching Control Documentation.
Create complex filter chains with multiple passes:
filters: {
gaussianBlur: {
type: 'render',
passes: [
{
// Horizontal blur pass
inputTexture: ['texture'],
outputTexture: 'tempTexture1',
shaderURL: 'shaders/blur-horizontal.wgsl'
},
{
// Vertical blur pass
inputTexture: ['tempTexture1'],
outputTexture: 'tempTexture2',
shaderURL: 'shaders/blur-vertical.wgsl'
},
{
// Composite to screen
inputTexture: ['tempTexture2'],
outputTexture: undefined, // Render to screen
shaderURL: 'shaders/composite.wgsl'
}
]
}
}Update filter parameters in real-time:
// Update single parameter
app.updateFilterBuffer('intensity', newValue);
// Update array/matrix parameter
app.updateFilterBuffer('colorMatrix', new Float32Array([
1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0
]));
// Re-render with updated parameters
await app.renderFilterPasses(filter);Configure custom textures with full control:
textures: {
customBuffer: {
label: 'Custom Processing Buffer',
format: 'rgba16float', // High precision
usage: GPUTextureUsage.TEXTURE_BINDING |
GPUTextureUsage.STORAGE_BINDING,
sampleCount: 4, // MSAA 4x
notes: 'Used for HDR processing'
}
}Available Formats:
rgba8unorm,rgba16float,rgba32float- Color texturesr8unorm,r16float,r32float- Single-channel textures
Texture Usage Flags:
GPUTextureUsage.TEXTURE_BINDING- Can be sampled in shadersGPUTextureUsage.RENDER_ATTACHMENT- Can be rendered toGPUTextureUsage.STORAGE_BINDING- Can be written in compute shadersGPUTextureUsage.COPY_SRC- Can be copied fromGPUTextureUsage.COPY_DST- Can be copied to
For more detailed information, see the following documentation:
- CLAUDE.md - AI agent guide with architecture details
- Stats API Quick Start - Statistics system quick start
- Stats API Guide - Comprehensive statistics documentation
- Performance Optimizations - Performance optimization details
- Caching Control - Caching system documentation
- Dynamic Buffer Resize - Buffer resize details
- Media Source Refactoring - Media source architecture
- Migration Guide v0.0.14 - v0.0.14 migration guide
This project is made available primarily as a resource for others to use and learn from. If you'd like to make modifications:
- Fork/Clone the Repository: Create your own copy of the codebase to customize for your needs
- Build Your Version: Make any modifications you need for your specific use case
- Learn and Adapt: Feel free to use any parts of this code in your own projects according to the license
While I'm not actively reviewing pull requests at this time, I hope you find this library useful as a starting point for your own WebGPU image processing implementations.
This project is licensed under the ISC License.