Monorepo for Aesthetic.Computer
aesthetic.computer
1# WebGPU Renderer Plan for aesthetic.computer
2
3## Current State Analysis
4
5### What's Already Built ✅
6
7**1. `/system/public/aesthetic.computer/lib/webgpu.mjs`** (381 lines)
8A foundational WebGPU renderer with:
9- WebGPU initialization with adapter/device setup
10- Canvas context configuration with alpha blending
11- **Clear pipeline** - full-screen clear with configurable color
12- **Line pipeline** - basic 1px line rendering in pixel coordinates
13- Proper uniform buffers (color, resolution) with WGSL shaders
14- Graceful fallback when WebGPU unavailable
15
16**2. BIOS Integration (`bios.mjs`)**
17- WebGPU module imported at line 41
18- `initWebGPU(canvas)` async initializer (line 1611-1617)
19- Message handler for `webgpu-command` (lines 10353-10355)
20
21**3. Disk API (`disk.mjs`)**
22- `$commonApi.webgpu` object with:
23 - `enabled` flag to disable CPU renderer
24 - `clear(r, g, b, a)` function
25 - `line(x1, y1, x2, y2, r, g, b, a)` function
26 - `render()` function
27- Auto-routing in `ink()` and `wipe()` when `webgpu.enabled = true`
28
29### Existing CPU Renderer Capabilities (graph.mjs)
30
31The current `graph.mjs` has sophisticated features that WebGPU needs to match/accelerate:
32
33**Pixel Manipulation:**
34- `plot(x, y)` - single pixel
35- `point(x, y)` - with pan translation
36- `pixel(x, y)` - read pixel color
37- `copy()` / `paste()` - buffer blitting with alpha, scaling, rotation
38- `copyRow()` / `copyRegion()` - optimized sub-region operations
39
40**Primitives:**
41- `line()` - Bresenham with thickness support
42- `lineh()` - horizontal line (fast path)
43- `lineAngle()` - angle-based line
44- `circle()` - filled/outline with thickness
45- `rect()` / `box()` - rectangles
46- `poly()` / `pline()` - polylines with thickness
47
48**Sprite/Texture:**
49- `paste(from, destX, destY, scale, blit)` - full sprite system
50 - Supports ImageBitmap, canvas buffers, pixel arrays
51 - Integer scaling (1-8x) with fast nearest-neighbor path
52 - Rotation via transform object `{ scale, angle, anchor, crop }`
53 - Alpha blending
54- `bitmapPixelCache` - WeakMap cache for ImageBitmap pixel extraction
55
56**Text System (type.mjs + graph.mjs):**
57- `printLine()` - renders text using `draw()` function
58- `draw()` - interprets glyph command lists:
59 - BDF fonts (unifont, MatrixChunky8) → pixel arrays
60 - Vector fonts → `line` and `point` commands
61 - Supports rotation, scaling, thickness
62- Typeface glyphs are just drawing commands (`{name: "line", args: [x1,y1,x2,y2]}`)
63- **Key insight:** Text can be accelerated by accelerating the underlying primitives!
64
65**Effects:**
66- `spin()` - radial blur/rotation with SIMD-like batching
67- `noise16()` variants - procedural noise
68- Mask system (`activeMask`) for clipping
69
70### What's Missing / Needs Work 🚧
71
72**1. Core Primitives for WebGPU**
73- [ ] Rectangle fill (`rect`) - enables text backgrounds
74- [ ] Circle/ellipse (`circle`, `oval`)
75- [ ] Filled polygon
76- [ ] Thick lines (line width > 1px)
77- [ ] Point rendering (single pixel)
78
79**2. Sprite/Texture Support** (HIGH PRIORITY)
80Since `paste()` is heavily used:
81- [ ] Texture upload from ImageBitmap/Uint8ClampedArray
82- [ ] Textured quad rendering
83- [ ] Nearest-neighbor sampling for pixel art aesthetic
84- [ ] Alpha blending modes
85
86**3. Text Rendering Strategy**
87Two approaches:
881. **Primitive-based:** Accelerate `point()` and `line()`, text rendering follows automatically
892. **Glyph atlas:** Pre-render glyphs to texture atlas, sample as textured quads
90
91**4. Compute Shader Rasterization** (from tutorial)
92The current implementation uses traditional render pipelines. For maximum flexibility and performance, a compute shader approach would enable:
93- Atomic depth testing
94- Custom blending modes (averaging, XOR, etc.)
95- Order-independent transparency
96- Performance wins for many small triangles
97
98**5. Command Batching**
99Current implementation executes commands immediately. Should batch for performance:
100- Accumulate commands during frame
101- Submit single command buffer at end of frame
102- Reuse buffers between frames
103
104---
105
106## Tutorial Reference Summary
107
108The [WebGPU Compute Rasterizer tutorial](https://github.com/OmarShehata/webgpu-compute-rasterizer) demonstrates:
109
110### Architecture
111```
112[Compute Pass] → outputColorBuffer (storage buffer)
113 ↓
114[Fullscreen Pass] → reads buffer → draws to screen
115```
116
117### Key Techniques
1181. **Storage Buffers** for pixel data (`WIDTH * HEIGHT * 3` for RGB)
1192. **Barycentric Coordinates** for triangle fill
1203. **Atomic Operations** (`atomicMin`) for depth testing without race conditions
1214. **Workgroup Dispatch** - one compute thread per triangle
122
123### Code Patterns Worth Adopting
124
125**Triangle Fill (WGSL):**
126```wgsl
127fn barycentric(v1: vec3<f32>, v2: vec3<f32>, v3: vec3<f32>, p: vec2<f32>) -> vec3<f32> {
128 let u = cross(
129 vec3<f32>(v3.x - v1.x, v2.x - v1.x, v1.x - p.x),
130 vec3<f32>(v3.y - v1.y, v2.y - v1.y, v1.y - p.y)
131 );
132 if (abs(u.z) < 1.0) { return vec3<f32>(-1.0, 1.0, 1.0); }
133 return vec3<f32>(1.0 - (u.x+u.y)/u.z, u.y/u.z, u.x/u.z);
134}
135
136fn draw_triangle(v1: vec3<f32>, v2: vec3<f32>, v3: vec3<f32>) {
137 let min_max = get_min_max(v1, v2, v3);
138 for (var x = startX; x <= endX; x++) {
139 for (var y = startY; y <= endY; y++) {
140 let bc = barycentric(v1, v2, v3, vec2<f32>(f32(x), f32(y)));
141 if (bc.x >= 0.0 && bc.y >= 0.0 && bc.z >= 0.0) {
142 color_pixel(x, y, color);
143 }
144 }
145 }
146}
147```
148
149**Atomic Depth Testing:**
150```wgsl
151struct ColorBuffer {
152 values: array<atomic<u32>>,
153};
154
155fn color_pixel(x: u32, y: u32, r: u32, g: u32, b: u32) {
156 let pixelID = u32(x + y * u32(uniforms.screenWidth)) * 3u;
157 atomicMin(&outputColorBuffer.values[pixelID + 0u], r);
158 // ... etc
159}
160```
161
162---
163
164## Implementation Phases
165
166### Phase 1: Solidify Current Renderer ✨
167**Goal:** Make `webgpu.mjs` useful for basic pieces
168
169- [ ] Add `rect(x, y, w, h, color)` with fill
170- [ ] Add `circle(x, y, radius, color)` with fill
171- [ ] Add thick line support (line width uniform)
172- [ ] Test with a demo piece that uses `webgpu.enabled = true`
173
174### Phase 2: Compute Rasterizer Foundation 🔧
175**Goal:** Set up compute-based rendering pipeline
176
177- [ ] Create dual-buffer system (color storage + screen texture)
178- [ ] Implement fullscreen quad pass to display storage buffer
179- [ ] Port clear pass to compute shader
180- [ ] Add basic triangle fill using barycentric coordinates
181
182### Phase 3: Full 2D Primitive Set 📐
183**Goal:** Replace all CPU drawing with GPU
184
185- [ ] Filled rectangles via compute
186- [ ] Filled circles via distance function
187- [ ] Lines with thickness (quad-based)
188- [ ] Polygons via triangle fan
189- [ ] Depth buffer for proper layering
190
191### Phase 4: Advanced Features 🚀
192**Goal:** Enable effects not possible with CPU renderer
193
194- [ ] Custom blend modes (multiply, screen, overlay)
195- [ ] Per-pixel effects (blur, glow, distortion)
196- [ ] Text rendering with SDF fonts
197- [ ] Instanced rendering for particles
198- [ ] Real-time filters on pixel buffer
199
200---
201
202## Questions to Answer
203
2041. **Canvas Sizing:** How does `webgpu.mjs` handle resolution changes? Should it match the low-res aesthetic or render at native resolution?
205
2062. **CPU/GPU Hybrid:** When `webgpu.enabled = true`, should ALL rendering go through GPU, or should we support hybrid mode?
207
2083. **Compatibility:** WebGPU is still experimental. What's the fallback strategy?
209 - WebGL2 via existing `2d.mjs`?
210 - CPU rendering via canvas?
211
2124. **Memory Budget:** How large can the storage buffer be for high-res displays?
213
214---
215
216## Test Piece Idea
217
218Create `system/public/aesthetic.computer/disks/webgpu-test.mjs`:
219
220```javascript
221// webgpu-test.mjs - WebGPU Renderer Demo
222export const desc = "WebGPU renderer test";
223
224export function boot({ webgpu }) {
225 webgpu.enabled = true;
226}
227
228export function paint({ webgpu, screen }) {
229 webgpu.clear(20, 20, 40, 255);
230
231 // Draw some lines
232 for (let i = 0; i < 10; i++) {
233 webgpu.line(
234 10 + i * 10, 10,
235 screen.width - 10, 10 + i * 20,
236 255, 100 + i * 15, 50, 255
237 );
238 }
239}
240
241export function act({ event: e }) {
242 // Handle input
243}
244```
245
246---
247
248## Related Files
249
250- `/workspaces/aesthetic-computer/gpu/RESEARCH.md` - Original research doc
251- `/workspaces/aesthetic-computer/system/public/aesthetic.computer/lib/2d.mjs` - Disabled WebGL2 renderer
252- `/workspaces/aesthetic-computer/system/public/aesthetic.computer/lib/3d.mjs` - ThreeJS renderer (for reference)
253- `/workspaces/aesthetic-computer/TODO.txt` line 627 - WebGPU rasterizer TODO
254- `/workspaces/aesthetic-computer/TODO.txt` line 1491 - WebGL2/WebGPU Backend TODO
255
256---
257
258## Next Steps
259
2601. **Create test piece** to validate current `clear` and `line` commands work
2612. **Add `rect` primitive** as first compute-based fill
2623. **Set up command batching** architecture
2634. **Profile performance** vs CPU renderer
264
265---
266
267*Last updated: November 26, 2025*