Electrobun WebGPU
Use when working with WebGPU in Electrobun — GpuWindow, WGPUView, WGSL shaders, KEEPALIVE pattern, render loops, FFI pointer management, and GPU buffer serialization.
Packaged view
This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.
Install command
npx @skill-hub/cli install milady-ai-milaidy-electrobun-webgpu
Repository
Skill path: .claude/plugins/electrobun-dev/skills/electrobun-webgpu
Use when working with WebGPU in Electrobun — GpuWindow, WGPUView, WGSL shaders, KEEPALIVE pattern, render loops, FFI pointer management, and GPU buffer serialization.
Open repositoryBest for
Primary workflow: Ship Full Stack.
Technical facets: Full Stack.
Target audience: everyone.
License: Unknown.
Original source
Catalog source: SkillHub Club.
Repository owner: milady-ai.
This is still a mirrored public skill entry. Review the repository before installing into production workflows.
What it helps with
- Install Electrobun WebGPU into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
- Review https://github.com/milady-ai/milaidy before adding Electrobun WebGPU to shared team environments
- Use Electrobun WebGPU for development workflows
Works across
Favorites: 0.
Sub-skills: 0.
Aggregator: No.
Original source / Raw SKILL.md
---
name: Electrobun WebGPU
description: Use when working with WebGPU in Electrobun — GpuWindow, WGPUView, WGSL shaders, KEEPALIVE pattern, render loops, FFI pointer management, and GPU buffer serialization.
version: 1.0.0
---
# Electrobun 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:**
```typescript
// electrobun.config.ts
mac: { bundleWGPU: true },
win: { bundleWGPU: true },
linux: { bundleWGPU: true },
```
## GpuWindow Setup
```typescript
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.
```typescript
// 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
```typescript
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
```wgsl
// 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).
```typescript
// 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
1. **No KEEPALIVE** → segfault or silent corruption mid-render.
2. **`bundleWGPU: false`** → runtime error; Electrobun won't include the WGPU native library.
3. **Not recreating swap chain on resize** → distorted rendering after window resize.
4. **Forgetting `little-endian: true`** in `DataView.setFloat32` → garbage GPU data on most platforms.
5. **Blocking the render loop** → use async I/O outside the render interval, never inside.