About electrobun-webgpu
This skill should be used when the user asks about "electrobun WebGPU", "GpuWindow", "WGPUView", "WGSL shaders", "KEEPALIVE pattern", "electrobun GPU", render loops in electrobun, FFI pointer management, or GPU buffer serialization.
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ dexploarer/ plugin-electrobun-dev --skill electrobun-webgpuAfter installing, type/electrobun-webgpuin Claude Code to confirm it loaded.
Details
From the project READMEElectrobun WebGPU Patterns
Electrobun wraps WGPU (a Rust WebGPU abstraction) via Bun FFI. GPU windows bypass the webview entirely — they render directly to a native surface.
Config Requirement
Always required before WebGPU code will work:
// electrobun.config.ts
mac: { bundleWGPU: true },
win: { bundleWGPU: true },
linux: { bundleWGPU: true },
GpuWindow Setup
import { GpuWindow } from "electrobun/bun";
const gpuWin = new GpuWindow({
title: "GPU App",
frame: { width: 800, height: 600 },
centered: true,
});
const view = gpuWin.createView(); // WGPUView
KEEPALIVE — Critical Pattern
Bun's GC will collect FFI pointers unless you hold a reference to them. Without KEEPALIVE, your app will crash mid-render with a segfault.
// ALWAYS create this array and push every GPU object into it
const KEEPALIVE: unknown[] = [];
const adapter = await navigator.gpu.requestAdapter();
KEEPALIVE.push(adapter);
const device = await adapter.requestDevice();
KEEPALIVE.push(device);
const pipeline = device.createRenderPipeline({ /* ... */ });
KEEPALIVE.push(pipeline);
const buffer = device.createBuffer({ /* ... */ });
KEEPALIVE.push(buffer);
Render Loop Pattern
const FRAME_MS = 16; // ~60fps
function renderFrame() {
// 1. Update uniform buffer with current state
const data = new ArrayBuffer(32);
const view = new DataView(data);
view.setFloat32(0, performance.now() / 1000, true); // time
view.setFloat32(4, canvas.width, true); // resolution.x
view.setFloat32(8, canvas.height, true); // resolution.y
view.setFloat32(12, mouseX, true); // mouse.x
view.setFloat32(16, mouseY, true); // mouse.y
device.queue.writeBuffer(uniformBuffer, 0, data);
// 2. Create command encoder
const encoder = device.createCommandEncoder();
KEEPALIVE.push(encoder);
// 3. Begin render pass
const pass = encoder.beginRenderPass({
colorAttachments: [{
view: context.getCurrentTexture().createView(),
clearValue: { r: 0, g: 0, b: 0, a: 1 },
loadOp: "clear",
storeOp: "store",
}],
});
// 4. Draw
pass.setPipeline(pipeline);
pass.setVertexBuffer(0, vertexBuffer);
pass.draw(3);
pass.end();
// 5. Submit
device.queue.submit([encoder.finish()]);
}
setInterval(renderFrame, FRAME_MS);
WGSL Shader Structure
// Vertex shader
struct VertexOutput {
@builtin(position) position: vec4f,
@location(0) uv: vec2f,
}
@vertex
fn vs_main(@builtin(vertex_index) idx: u32) -> VertexOutput {
// Full-screen triangle
var positions = array<vec2f, 3>(
vec2f(-1.0, -1.0),
vec2f( 3.0, -1.0),
vec2f(-1.0, 3.0),
);
var out: VertexOutput;
out.position = vec4f(positions[idx], 0.0, 1.0);
out.uv = (positions[idx] + vec2f(1.0)) * 0.5;
return out;
}
@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4f {
// TODO: replace with your shader logic
return vec4f(in.uv, 0.5, 1.0);
}
Vertex Buffer with DataView
GPU structs must be manually serialized. Vertex layout: [x, y, time, res.x, res.y, mouse.x, mouse.y] (all f32).
// 3 vertices × 7 floats × 4 bytes = 84 bytes
const vertexData = new ArrayBuffer(3 * 7 * 4);
const dv = new DataView(vertexData);
// Full-screen triangle vertices
const verts = [
[-1, -1], [3, -1], [-1, 3],
];
verts.forEach(([x, y], i) => {
const offset = i * 7 * 4;
dv.setFloat32(offset + 0, x, true); // position.x
dv.setFloat32(offset + 4, y, true); // position.y
dv.setFloat32(offset + 8, time, true); // time
dv.setFloat32(offset + 12, width, true);
dv.setFloat32(offset + 16, height, true);
dv.setFloat32(offset + 20, mouseX, true);
dv.setFloat32(offset + 24, mouseY, true);
});
Common Mistakes
- No KEEPALIVE → segfault or silent corruption mid-render.
bundleWGPU: false→ runtime error; Electrobun won't include the WGPU native library.- Not recreating swap chain on resize → distorted rendering after window resize.
- Forgetting
little-endian: trueinDataView.setFloat32→ garbage GPU data on most platforms. - Blocking the render loop → use async I/O outside the render interval, never inside.