Back to skills
SkillHub ClubShip Full StackFull Stack

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.

Stars
270
Hot score
98
Updated
March 20, 2026
Overall rating
C3.2
Composite score
3.2
Best-practice grade
B72.4

Install command

npx @skill-hub/cli install milady-ai-milaidy-electrobun-webgpu

Repository

milady-ai/milaidy

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 repository

Best 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

Claude CodeCodex CLIGemini CLIOpenCode

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.