JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 10
  • Score
    100M100P100Q70130F
  • License MIT

WebGPU for Node.js via wgpu-rs (modern, lightweight alternative to Dawn)

Package Exports

  • @sylphx/webgpu
  • @sylphx/webgpu/webgpu.js

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@sylphx/webgpu) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

@sylphx/webgpu

npm version License: MIT

Production-ready WebGPU for Node.js & Bun - 100% standard-compliant, built with Rust + wgpu

โœจ What is @sylphx/webgpu?

The modern, lightweight WebGPU implementation for Node.js. Use the same WebGPU API in both Node.js and browsers - write once, run everywhere.

const { Gpu, GPUBufferUsage } = require('@sylphx/webgpu')

// Initialize GPU (identical to browser API)
const gpu = Gpu()
const adapter = await gpu.requestAdapter()
const device = await adapter.requestDevice()

// Create buffer (100% WebGPU standard)
const buffer = device.createBuffer({
    size: 256,
    usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST
})

// Run compute shader
const encoder = device.createCommandEncoder()
const pass = encoder.beginComputePass()
pass.setPipeline(pipeline)
pass.setBindGroup(0, bindGroup)
pass.dispatchWorkgroups(64)
pass.end()
device.queue.submit([encoder.finish()])

๐Ÿš€ Why Choose @sylphx/webgpu?

Feature @sylphx/webgpu @kmamal/gpu (Dawn)
WebGPU Standard โœ… 100% compliant โš ๏ธ Custom API
Binary Size 1.9-4.6MB 50-150MB
Build Time ~30 seconds 1-3 hours
Code Portability โœ… Browser compatible โŒ Node.js only
Implementation Firefox's wgpu (Rust) Chrome's Dawn (C++)
Toolchain Simple (Cargo) Complex (depot_tools)
Status v1.0 - Production ready 0.x - Pre-release

Key Advantages

โœ… 100% WebGPU Standard - Share code between Node.js and browsers โœ… Production Ready - v1.0.1 stable release with 58 tests, 100% pass rate โœ… Ultra Lightweight - 2-5MB binaries vs 100MB+ alternatives โœ… Modern Stack - Rust + wgpu (used by Firefox, Deno, Bevy) โœ… Cross-Platform - 6 prebuilt platforms (macOS, Linux, Windows, ARM64) โœ… Well Tested - Comprehensive test suite covering all features

๐Ÿ“ฆ Installation

npm install @sylphx/webgpu

Requirements:

  • Node.js 18+ or Bun 1.0+
  • No build tools needed (prebuilt binaries included)

Supported Platforms:

  • macOS (x64, ARM64/M1/M2/M3)
  • Linux (x64, ARM64)
  • Windows (x64, ARM64)
  • FreeBSD, Android (via source build)

๐ŸŽฏ Quick Start

Basic GPU Setup

const { Gpu } = require('@sylphx/webgpu')

async function main() {
    // Create GPU instance
    const gpu = Gpu()

    // Request adapter (automatically selects best GPU)
    const adapter = await gpu.requestAdapter({
        powerPreference: 'high-performance'
    })

    console.log('GPU:', adapter.info.name)
    console.log('Backend:', adapter.info.backend)

    // Request device
    const device = await adapter.requestDevice()

    console.log('โœ… WebGPU ready!')
}

main()

Compute Shader Example

const { Gpu, GPUBufferUsage } = require('@sylphx/webgpu')

async function runCompute() {
    const gpu = Gpu()
    const adapter = await gpu.requestAdapter()
    const device = await adapter.requestDevice()

    // Create buffers
    const size = 256
    const input = device.createBuffer({
        size,
        usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST
    })

    const output = device.createBuffer({
        size,
        usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC
    })

    // Create compute shader (WGSL)
    const shader = device.createShaderModule({
        code: `
            @group(0) @binding(0) var<storage, read> input: array<f32>;
            @group(0) @binding(1) var<storage, read_write> output: array<f32>;

            @compute @workgroup_size(64)
            fn main(@builtin(global_invocation_id) id: vec3<u32>) {
                output[id.x] = input[id.x] * 2.0;
            }
        `
    })

    // Create bind group layout
    const layout = device.createBindGroupLayout({
        entries: [
            { binding: 0, visibility: 4, buffer: { type: 'read-only-storage' } },
            { binding: 1, visibility: 4, buffer: { type: 'storage' } }
        ]
    })

    // Create bind group
    const bindGroup = device.createBindGroup({
        layout,
        entries: [
            { binding: 0, resource: { buffer: input } },
            { binding: 1, resource: { buffer: output } }
        ]
    })

    // Create pipeline
    const pipeline = device.createComputePipeline({
        layout: device.createPipelineLayout({ bindGroupLayouts: [layout] }),
        compute: { module: shader, entryPoint: 'main' }
    })

    // Execute compute shader
    const encoder = device.createCommandEncoder()
    const pass = encoder.beginComputePass()
    pass.setPipeline(pipeline)
    pass.setBindGroup(0, bindGroup)
    pass.dispatchWorkgroups(4) // 4 * 64 = 256 threads
    pass.end()

    device.queue.submit([encoder.finish()])

    console.log('โœ… Compute shader executed!')
}

runCompute()

Render Pipeline Example

const { Gpu, GPUBufferUsage, GPUTextureUsage } = require('@sylphx/webgpu')

async function renderTriangle() {
    const gpu = Gpu()
    const adapter = await gpu.requestAdapter()
    const device = await adapter.requestDevice()

    // Create vertex buffer
    const vertices = new Float32Array([
        // x,    y,     r,   g,   b
         0.0,  0.5,   1.0, 0.0, 0.0,  // top (red)
        -0.5, -0.5,   0.0, 1.0, 0.0,  // bottom left (green)
         0.5, -0.5,   0.0, 0.0, 1.0   // bottom right (blue)
    ])

    const vertexBuffer = device.createBuffer({
        size: vertices.byteLength,
        usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
        mappedAtCreation: true
    })

    new Float32Array(vertexBuffer.getMappedRange()).set(vertices)
    vertexBuffer.unmap()

    // Create shader
    const shader = device.createShaderModule({
        code: `
            struct VertexInput {
                @location(0) position: vec2f,
                @location(1) color: vec3f
            }

            struct VertexOutput {
                @builtin(position) position: vec4f,
                @location(0) color: vec3f
            }

            @vertex
            fn vs_main(in: VertexInput) -> VertexOutput {
                var out: VertexOutput;
                out.position = vec4f(in.position, 0.0, 1.0);
                out.color = in.color;
                return out;
            }

            @fragment
            fn fs_main(in: VertexOutput) -> @location(0) vec4f {
                return vec4f(in.color, 1.0);
            }
        `
    })

    // Create render pipeline
    const pipeline = device.createRenderPipeline({
        layout: 'auto',
        vertex: {
            module: shader,
            entryPoint: 'vs_main',
            buffers: [{
                arrayStride: 20,
                attributes: [
                    { shaderLocation: 0, offset: 0, format: 'float32x2' },
                    { shaderLocation: 1, offset: 8, format: 'float32x3' }
                ]
            }]
        },
        fragment: {
            module: shader,
            entryPoint: 'fs_main',
            targets: [{ format: 'rgba8unorm' }]
        }
    })

    // Create texture for rendering
    const texture = device.createTexture({
        size: { width: 512, height: 512 },
        format: 'rgba8unorm',
        usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC
    })

    // Render triangle
    const encoder = device.createCommandEncoder()
    const pass = encoder.beginRenderPass({
        colorAttachments: [{
            view: texture.createView(),
            loadOp: 'clear',
            storeOp: 'store',
            clearValue: { r: 0.1, g: 0.1, b: 0.1, a: 1.0 }
        }]
    })

    pass.setPipeline(pipeline)
    pass.setVertexBuffer(0, vertexBuffer)
    pass.draw(3)
    pass.end()

    device.queue.submit([encoder.finish()])

    console.log('โœ… Triangle rendered!')
}

renderTriangle()

๐Ÿ“š Complete Examples

Check the examples/ directory for more:

Run any example:

node examples/compute.js
# or with Bun (faster startup)
bun examples/compute.js

๐ŸŽ“ API Documentation

Full WebGPU Standard API

The API is 100% compliant with the W3C WebGPU specification. Code written for browsers works identically in Node.js.

Core Objects:

  • Gpu - Entry point (equivalent to navigator.gpu)
  • GPUAdapter - Physical GPU representation
  • GPUDevice - Logical device for GPU operations
  • GPUBuffer - GPU memory buffer
  • GPUTexture - GPU texture (images)
  • GPUSampler - Texture sampling configuration
  • GPUShaderModule - Compiled WGSL shader
  • GPUBindGroup - Resource bindings
  • GPUPipelineLayout - Pipeline resource layout
  • GPUComputePipeline - Compute shader pipeline
  • GPURenderPipeline - Render pipeline
  • GPUCommandEncoder - Command recording
  • GPUComputePassEncoder - Compute pass recording
  • GPURenderPassEncoder - Render pass recording
  • GPUQueue - Command submission queue

Constants (WebGPU Standard):

const {
    GPUBufferUsage,    // Buffer usage flags
    GPUTextureUsage,   // Texture usage flags
    GPUMapMode,        // Buffer map modes
    GPUShaderStage     // Shader stage flags
} = require('@sylphx/webgpu')

Key Differences from Browser

Entry Point:

// Browser
const adapter = await navigator.gpu.requestAdapter()

// Node.js (@sylphx/webgpu)
const { Gpu } = require('@sylphx/webgpu')
const gpu = Gpu()
const adapter = await gpu.requestAdapter()

Everything else is identical! All methods, properties, and descriptors match the browser API exactly.

๐ŸŒ Browser Compatibility

Share code between Node.js and browsers:

// Universal WebGPU code (works in both!)
export async function initGPU() {
    // Detect environment
    const gpu = typeof navigator !== 'undefined'
        ? navigator.gpu
        : require('@sylphx/webgpu').Gpu()

    const adapter = await gpu.requestAdapter()
    const device = await adapter.requestDevice()

    // All code below is identical!
    const buffer = device.createBuffer({
        size: 256,
        usage: GPUBufferUsage.STORAGE
    })

    return { device, buffer }
}

๐Ÿ”ง Advanced Features

GPU Profiling with Timestamp Queries

const querySet = device.createQuerySet({
    type: 'timestamp',
    count: 2
})

const encoder = device.createCommandEncoder()
encoder.writeTimestamp(querySet, 0)

// ... GPU work ...

encoder.writeTimestamp(querySet, 1)
device.queue.submit([encoder.finish()])

// Read timing results
const timings = await readTimestamps(querySet)
console.log(`GPU time: ${(timings[1] - timings[0]) / 1e6}ms`)

Indirect Draw (GPU-Driven Rendering)

// GPU generates its own draw commands
const indirectBuffer = device.createBuffer({
    size: 20,
    usage: GPUBufferUsage.INDIRECT | GPUBufferUsage.STORAGE
})

// Compute shader writes draw commands
// Render pass reads from buffer
pass.drawIndirect(indirectBuffer, 0)

Multiple Render Targets (Deferred Rendering)

const pass = encoder.beginRenderPass({
    colorAttachments: [
        { view: positionTexture.createView(), ... },   // G-buffer position
        { view: normalTexture.createView(), ... },     // G-buffer normal
        { view: albedoTexture.createView(), ... }      // G-buffer albedo
    ]
})

๐Ÿงช Testing

# Run all tests
npm test

# Watch mode
npm run test:watch

# Coverage report
npm run test:coverage

Test Suite:

  • 58 comprehensive tests
  • 100% pass rate
  • Covers all WebGPU features
  • Real GPU operations (not mocked)

๐Ÿ—๏ธ Building from Source

# Install Rust (if not already installed)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# Clone repository
git clone https://github.com/SylphxAI/webgpu.git
cd webgpu

# Install dependencies
npm install

# Build native bindings (~30 seconds clean build)
npm run build

# Run tests
npm test

๐Ÿ“Š Performance

Binary Size (Actual Prebuilt Binaries):

  • @sylphx/webgpu: 1.9-4.6MB (macOS: 1.9-2.2MB, Linux: 3.0-3.5MB, Windows: 4.1-4.6MB)
  • @kmamal/gpu: 50-150MB (Dawn binaries)

Build Time (Clean Build):

  • @sylphx/webgpu: ~30 seconds (Cargo release build)
  • @kmamal/gpu: 1-3 hours (Dawn + depot_tools)

Runtime Performance:

  • GPU operations: Zero overhead (thin wrapper)
  • CPU overhead: <10% for descriptor transformation
  • Compute/Render: Limited by GPU, not bindings

๐Ÿ› ๏ธ Architecture

User Code (WebGPU Standard API)
    โ†“
webgpu.js (JavaScript wrapper - transforms descriptors)
    โ†“
index.js (napi-rs native bindings)
    โ†“
Rust (wgpu implementation)
    โ†“
GPU Drivers (Metal/Vulkan/DX12)

The JavaScript wrapper provides 100% standard WebGPU API while the Rust layer uses optimized flat signatures for napi-rs compatibility.

๐Ÿค Contributing

We welcome contributions! See CONTRIBUTING.md for guidelines.

Areas for Contribution:

  • ๐Ÿ“š Additional examples and tutorials
  • ๐Ÿงช More test cases and benchmarks
  • ๐Ÿ”Œ Integration with frameworks (Three.js, Babylon.js, etc.)
  • ๐Ÿ“– Documentation improvements
  • ๐Ÿ› Bug reports and fixes

๐Ÿ“„ License

MIT ยฉ SylphxAI

๐Ÿ”— Resources

โญ Star History

If you find this project useful, please consider giving it a star on GitHub!


Ready to use WebGPU in Node.js?

npm install @sylphx/webgpu

v1.0.1 - Production Ready ๐Ÿš€