Add core-powered web sculpting studio with tablet controls

Add pressure-aware worker sculpting, bounded shared input, project files and history, and compact icon navigation with an XYZ orbit gizmo.

Amp-Thread-ID: https://ampcode.com/threads/T-01a0b4b4-202c-73f1-984d-9a55c88f9c83
Co-authored-by: Heaust Azure <heaust.azure@gmail.com>
This commit is contained in:
Amp
2026-09-28 08:53:50 +00:00
co-authored by heaust
parent eb8aa0e9dc
commit 347b676cf1
25 changed files with 5468 additions and 1 deletions
+48
View File
@@ -0,0 +1,48 @@
// One latest target in a core shared row. Versions are snapshot tokens, not a
// sample count. The producer never touches the consumer's acknowledgement.
export class Mailbox {
constructor({ buffer, descriptor }) {
this.words = new Int32Array(buffer, descriptor.offset, 16);
this.scratch = new ArrayBuffer(4);
this.bits = new Int32Array(this.scratch);
this.float = new Float32Array(this.scratch);
}
encode(value) {
this.float[0] = value;
return this.bits[0];
}
decode(value) {
this.bits[0] = value;
return this.float[0];
}
write(stroke, sample, seed) {
const version = (Atomics.load(this.words, 0) + 2) | 0;
Atomics.store(this.words, 0, version - 1);
Atomics.store(this.words, 2, stroke);
Atomics.store(this.words, 3, this.encode(sample.x));
Atomics.store(this.words, 4, this.encode(sample.y));
Atomics.store(this.words, 5, this.encode(sample.pressure));
Atomics.store(this.words, 6, this.encode(seed ?? 0));
Atomics.store(this.words, 7, seed === undefined ? 0 : 1);
Atomics.store(this.words, 0, version);
return version;
}
read(stroke) {
const a = Atomics.load(this.words, 0);
if (a & 1 || a === Atomics.load(this.words, 1)) return null;
const id = Atomics.load(this.words, 2);
const x = this.decode(Atomics.load(this.words, 3));
const y = this.decode(Atomics.load(this.words, 4));
const pressure = this.decode(Atomics.load(this.words, 5));
const seed = this.decode(Atomics.load(this.words, 6));
const hasSeed = Atomics.load(this.words, 7);
if (a !== Atomics.load(this.words, 0) || id !== stroke) return null;
return { version: a, x, y, pressure, seed: hasSeed ? seed : undefined };
}
acknowledge(version) {
Atomics.store(this.words, 1, version);
}
get processed() {
return Atomics.load(this.words, 1);
}
}