Commit graph

97 commits

Author SHA1 Message Date
Admin
79882b5fb6 fabric: a photo or a live camera to a fitted sewing pattern
apps/fabric runs SAM 3D Body in-process (install + licence through the hub panel), measures the rest-pose body mesh (libs/fabric/measure: 25 tape measurements from plane slices and landmarks) and drafts parametric patterns (libs/fabric/draft: T-shirt, A-line skirt, easy trousers; nesting, true-scale SVG, tiled A4 PDF). Live mode streams the webcam at a few frames a second, shows the posed body with the measurement rings riding on it, and re-drafts when the numbers settle. Measurements sit in a DataGrid that copies as tab-separated text.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-02 02:51:45 +02:00
Admin
cb97fc569f vj: the loop splat — a song sliced into per-stem, beat-quantized loops on the APC40 grid
Eight song sections by drums, bass, vocals, other and mix; launch and swap on the bar, right-click halves and quarters, per-cell waveforms with score blocks, a full-width score popup (drums, bass tab, melody with lyrics) played back through the piano and the sample kit, an NMF drum transcriber, Basic Pitch for pitched stems and Beat This! grid refinement when installed, beat jump and phase-flip buttons, a loading overlay for the grid, and the shared model install panel in INSTALL MODELS.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-02 02:51:44 +02:00
Admin
120b7e23ae score view: the engraver as a shared library, with drum and pitched score builders
libs/score_view carries the engraving, spacing, font and document code out of the score app so any app can show notation: a ScoreView widget with width/page/content fits, pan and zoom, a playhead, a dark palette, drum-key labels, lyrics under melody notes, and builders for drum, pitched and bass-tab scores. The score app uses it. Vector glyphs no longer write depth for transparent fragments, and the Metal screenshot staging sizes itself from the source texture.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-02 02:51:44 +02:00
Admin
97e9572f42 speech: one STT/TTS API on every platform, through the ai-hub
Apps ask the hub for a recognizer or a voice and get one; where it runs is
the hub's decision. AiHub::start_stt / start_tts return poll-driven
sessions shaped like the chat session. The Auto ladder is Whisper/Kokoro in
this process (weights present, machine election), on the machine node over
loopback, on a LAN node, else the OS engine; SpeechReach::Local is the
"don't reach out" knob. Audio always comes back as PCM: the app owns the
device.

Three layers:
- makepad-ai-speech is the whole speech model family, engines only.
  libs/voice (Whisper + Silero VAD) folds in as the `whisper` and `vad`
  modules next to kokoro and indextts, each a cargo feature; the Apple
  bridges and the Speaker/VoiceTranscriber selection leave it.
- makepad-system-speech (new) is the OS speech services as blocking fns:
  Apple SpeechAnalyzer/AVSpeechSynthesizer via Swift, Windows.Media.Speech*
  on the vendored bindings, Android SpeechRecognizer/TextToSpeech through
  MakepadSpeech.java (API 26 floor), espeak-ng on Linux. It models the two
  STT shapes honestly: PCM in (Whisper, Apple) versus an engine that owns
  the microphone (Android, Windows), with capabilities the caller reads.
- the hub grows speech sessions, in-process Whisper/Kokoro workers with the
  residency election, a `whisper` wire backend (stt domain, registry entry
  pinned to ggerganov/whisper.cpp) so a Mac can serve a Quest, and a
  `language` field on the generate request.

Consumers: the Window voice input runs on an STT session and switches to
engine-mic mode when the recognizer owns the microphone; converse's
SpeechOutput is a lazily started TTS session plus a pump thread; route
drops its private speech copy for converse; vj's lyrics fallback and the
alignment bakes call the engines directly.

Verified here: speech-roundtrip through the real sessions (Apple voice in,
in-process Whisper on Metal out, 4.3% WER); system-speech-test TTS->STT
verbatim; hub/converse/system-speech unit tests; msvc, aarch64-android and
linux-gnu cross-checks; Java against android-34. Windows, Android and Linux
bridges are compile-checked only.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-01 23:32:35 +02:00
Admin
9ff44e87a6 ai-hub: the body-native wiring, this time only the lane's hunks
The recut in a6341981d applied its patch against the wrong directory
and restored the six shared files to the previous tree without the
body-native hunks. This commit adds exactly those: the `body-native`
feature and optional dependency, the pinned `sam3dbody` registry entry
and its test, the backend arms and the module declaration. Working
tree untouched.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-01 23:03:47 +02:00
Admin
a6341981d8 ai-hub: the body-native commit carried a peer's in-flight hub hunks; put them back where they were
66e5e2f11 committed the working tree of the shared hub files and with it
another lane's uncommitted edits (a new domain, request fields, a
backend arm, manifest lines). This commit restores those files to the
previous tree plus only the body-native hunks. The working tree is
untouched: the peer's edits stay on disk as their uncommitted work,
exactly as before.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-01 23:03:20 +02:00
Admin
66e5e2f117 ai-hub: SAM 3D Body runs natively — sam3dbody on the body domain, oracle-matched end to end
model.rs closes the loop: crop -> backbone -> ray-conditioned context ->
six decoder steps, each through the pose head, the rig, the camera and
the projection, then the packet the sandbox already reads (kp3d/kp2d in
camera axes, the 204 rig parameters, global rotation, camera translation,
joint positions). Against the reference on the oracle image, on Metal:
3D keypoints within 1.7 mm, 2D within 0.4 px, rig parameters, camera
and rotation within 2e-3. packet.rs writes the JSON by hand with the
reference worker's rounding and field order.

The hub gains the `body-native` feature (default on): registry entry
`sam3dbody` pinned to the Comfy-Org repack by revision, size and sha,
body_native_backend.rs beside the subprocess reference backend with the
same live_step contract, the `body` capability advertised when the
feature is compiled, and a stubbed test double for the CPU-only tests.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-01 23:01:49 +02:00
Admin
915ce7c4e2 asset-server: the embed module — fully-local mode becomes shared
The VJ's attach-or-host logic (health probe, beacon listen, the main
store's server.lock as proof-of-life; loopback-only Host with the library
publisher, rooted in the main library when one exists) moves into
makepad-app-asset-server as embed::resolve, steered by <PREFIX>_ASSET_
EMBED/ROOT/PORT. vj/local_store.rs becomes the thin VJ-flavoured wrapper.
First second consumer: the sandbox.
2026-09-01 18:01:21 +02:00
Admin
0131ba49ed mpwm: --test-action launch-<app> — put one registered app on the desk, deterministically
the existing startup test hook learns the launcher's verb, which is how a
hidden instance hosts a real child for framebuffer verification.
2026-09-01 17:43:47 +02:00
Admin
df265f7a55 vj: a short console draws smaller — height joins the scale chain
console_scale defended the width and rearranged the very short, but
between the fold thresholds (~820 points) and the height the console
actually needs (~1000) lay a dead zone where the lower region simply
clipped — an mpwm tile at 844 points lost its rail from MAP down and half
a grid row. TARGET_HEIGHT_POINTS closes it: console_dpi now takes the
window's physical height and a short console shrinks exactly as a narrow
one does, to the same MIN_SCALE floor, before the folds take over —
measured in the shrunk points. A wide, short window still prefers the
lists-beside arrangement (judged at native scale, so the chain stays a
pure function of the physical window). The whole rule chain threads the
height; tests cover the tile shape that exposed this and the new ladder.
2026-09-01 17:43:47 +02:00
Admin
d5a6d64d1b vj: LIGHTS and ARCHIVE are rail chips under MAP — any content chip is the way back to the grid
The tiny icon page-tabs are gone. The special pages join the lane rail as
first-class chips: when one is up no content lane reads selected, and
clicking VIDEO (or any lane) flips the lower region back to the grid — the
persisted ARCHIVE page can no longer read as an empty library.
2026-09-01 17:21:35 +02:00
Admin
89fe23fb68 vj: the self-hosted store is the full host — the library publisher rides along, loopback only, no beacon
Attach still wins whenever discovery finds a server (health probe, UDP
beacon, main-store lock). When nothing answers, the VJ now boots the
standalone asset-server's Host instead of a bare AssetServer: catalog +
CAS plus the ai-content library publisher over local/ai_content_library,
so a self-hosted VJ sees the same rows asset-ui publishes — not an empty
seed store. The fallback root is the user's main library when one holds a
catalog (server.lock arbitrates the race; losing it means attach). Both
planes bind 127.0.0.1 and no beacon runs: a private host is not a thing
the network can discover. HostConfig grows a blob_refs knob so the VJ
keeps its reference-import policy; the store decision is logged at boot.
2026-09-01 17:09:26 +02:00
Admin
87474bd68d finance: a personal ledger, budgets and reports on the DataGrid
Squashed from work:
- finance: a personal ledger, budgets and reports on the DataGrid
- zero-warning sweep, round two — the first full-workspace pass
2026-09-01 16:46:35 +02:00
Admin
79ccb10e81 mp*: the treemap camera era — canon-space packing, 2.5D and orbit views, streaming walks, the qwen sidepanel; mpwm linux desktop integration; pdf_parse recovery
Squashed from work:
- pdf_parse: indirect /Length, and recover the glyph names a symbol font uses
- mpwm: make the Linux desktop integration usable (#1204)
- mpfiles: the size map goes all the way down, and remembers what it found
- mpfiles: the treemap walk streams every directory at every depth — no more opaque growing blocks over deep heavy subtr
- mpfiles: local qwen chat sidepanel over the treemap selection
- mpfiles: a camera over the treemap — scroll zooms at the cursor with real re-layout detail, drag pans, double-click fi
- mpfiles: a 2.5D treemap — the map extrudes by nesting depth under a 3d toolbar mode
- mpfiles: a real orbit camera over the block view — left-drag orbits, right-drag pans, click stays a click; the project
- mpfiles: the wheel glides, the filter stops paying per frame and per keystroke — measured weights cached against the t
- mpfiles: the 3d view wears no name tags — labels only on hover, the tooltip carries the numbers
- mpfiles: a camera gesture never re-flows the map — mid-motion the picture rides one rigid remap of the last layout, an
- mpfiles: a small zoom keeps the map still — the layout is only remade when the camera truly leaves it (a 1.3x detail b
- mpfiles: a camera reveal is not an arrival — cells the cull swings into view appear instantly at full strength, cells
- mpfiles: the treemap packs in canon space — the arrangement is zoom-invariant by construction; zoom only refines the t
- mpfiles: the map stops breathing — insets are fractions of map space, names reserve no room and float on scrims with a
- mpfiles: a camera settle is calm — zoom detail materialises in place, no arrival fade; the ceremony stays with filter
- mpfiles: camera motion animates nothing but the camera — flat labels freeze per relayout and ride the remap; a calm se
- mpfiles: a fast zoom-out never outruns the map — cull escape relayouts the same frame, the glide's destination is laid
- mpfiles: wheel zoom in the raised modes anchors on the tile top under the cursor, not the ground hiding behind it
- mpfiles: first consumer of the ai-hub — the ask panel rides AiHub::start_local_chat, its private engine is gone (aicor
- ai-hub: chats run the machine election — route to a serving holder, wait on a loading one, claim and publish when open
- zero-warning sweep, round two — the first full-workspace pass
- zero-warning sweep, round three — the model lanes and the deep examples

Co-authored-by: Alex <blackanger.z@gmail.com>
2026-09-01 16:46:34 +02:00
Admin
517308e676 vj: deck sync as a held lock, archive.org as a content source, frametween becomes a library, runs execute in the app
Squashed from work:
- vj decks: sync is a held lock — pinned master, per-pump rate servo
- vj: the deck explorer opens on music
- frametween: the VJ's in-betweener becomes a library — the whole mode set, one definition
- frametween: the gate says which tier moved the picture, from pixels
- frametween: BGRA words go in as they are, and a host may own the clock
- vj: archive.org as a content source
- platform: native file and save dialogs, in-house on all three desktops
- vj: rounder slider caps, and the cap body reads as a blob
- ai-hub: makepad-asset-ai becomes makepad-ai-hub at libs/ai/hub, the chat pane becomes makepad-chat-ui, the service bin
- vj: DREAM runs execute in the app — pipelines.rs becomes the run it used to watch (aicore §9 / F1)
- vj: plain generations execute in the app too — the store's job queue loses its last vj client (aicore §9 / F2)
- asset-creator: the runner — generate one thing and put it in the catalog, one implementation for every surface (aicore
- importer + asset-server host: the coordination era ends (aicore P7)
- client + chat dispatcher: the dead wire comes out (aicore P7/P8)
- zero-warning sweep, round five — vj and chat-ui
- zero-warning sweep, round six — three cascades
- zero-warning sweep, round seven — the last two
2026-09-01 16:46:34 +02:00
Admin
c460905e2a maps + route: the self-baking Amsterdam test map, reversible mkmap weave, the rtsmap generator, road faces, guarded first-run
Squashed from work:
- rtsmap: one seeded generator for tiled strategy maps
- map: bake a runnable Amsterdam test map from inside the route app
- route: the first-run test map starts itself, and its buttons are guarded
- map: the test map bakes its road faces too, from the same shared pass
- map_tiles: mkmap-extract — the weave is reversible, world-cells reconstructible from world.mkmap
- route: the local dispatcher rides the hub — its copied engine is deleted, limits preserved (aicore P1)
- route + converse: off makepad_ai — the Agent seam moves to converse, route's cloud dispatcher rides the hub's Claude p
- ai-hub: chats run the machine election — route to a serving holder, wait on a loading one, claim and publish when open
- libs: the zero-warning sweep — stitch casts say what they mean, xatlas keeps upstream's surface quietly
2026-09-01 16:46:33 +02:00
Admin
145d0b1fe2 score: the notation suite — engraving, layout, playback, midi and musicxml import, the physical piano model, and the score app
Squashed from work:
- score: a headless music engraving, playback and notation engine
- score: the notation app — pianist mode, editing, playback
- piano_model: it was a plucked string by construction, and 20 voicings
- score: one document you can pan, zoom and navigate
- piano_model: the body tap was a click, and the objective was rewarding noise
- score: add the sound panel and library modules
- piano_model: a second engine, and the attack that finally sounded right
- score: two instruments, reverb and brightness — and the rest of the panel gone
- score model: a note remembers how it was struck, and the score remembers the pedal
- score import: keep the velocities and the pedal the file was carrying
- score playback: play the performance, not a flattened copy of it
- score ui: the music list moves to the sidebar, and the view stops fighting itself
- score: the application ships its font and eight performances
- piano_model: a limiter that rides the music, so the knee stops shaping chords
- piano_model: the forte bell was the treble's dynamic slope, and the bass was dying at its own prompt rate
- piano_model: the bridge decides each partial's decay, and a fixed multiplier cannot say that
- piano_model: each partial gets its own two coupled modes, from the eigen algebra
- piano_model: a median that fell between the peaks made every bass partial a drain
- score-ai: LocalBroker — the seam's in-process implementation over the session engine (aicore P8)
- client + chat dispatcher: the dead wire comes out (aicore P7/P8)
- score_pdf: the score model grew a pedal map — the pdf importer initialises it
- libs: the zero-warning sweep — stitch casts say what they mean, xatlas keeps upstream's surface quietly
- score app: the shipped-piece test speaks the PERFORMANCES table
- zero-warning sweep, round three — the model lanes and the deep examples
2026-09-01 16:46:32 +02:00
Admin
e37c263b9c ai backbone: the hub era — makepad_ai deleted, every backend is a hub pipe; machine residency elections, job leases, ETA placement; the store only stores; creator pipelines run in the app (aicore)
Squashed from work:
- asset-ai: FastH3 4-step fast video backend; clip keyframes on the wire
- asset-ui: loop video chains — text→image→video that ends where it began
- h3: safetensors -> pruned-Q4_K GGUF quantizer for the 24GB DiT tiers
- h3_quant_gguf verify: row-error gates calibrated to the measured Q4_K floor
- asset-ai realtime: the feedback loop — the source anchors, the drifted frame inits
- asset-ai realtime: a feedback loop survives a resize and travels by default
- asset-ai realtime: the feedback loop frees itself from the feed handshake and pauses for its listener
- asset-ai realtime: the outbound encode leaves the loop's critical path
- asset-ai ocr: the ocr domain — Chandra 2 at page resolution, and the tower goes planner-owned
- llm slots: a lane can hold an image span — embedding prefill and a rope cursor of its own
- vision tower on CUDA: the encode leg gets its two missing kernels
- llm/ocr: one M-RoPE grid encoder for both image paths, and a livelock made an error
- vision tower on CUDA: the f16 GEMM keeps the precision it was throwing away
- live: a feed that moves box takes its trip with it — one seed image
- vision tower on CUDA: the tiled attention becomes bit-exact, and tensor cores go
- llm prefill on CUDA: the MMA attention kernel gets the tile a 4-to-1 model needs
- asset-ai ocr: the CUDA encode lane joins the integration — vision-parity sits beside run's three arms, and the kernels
- Merge branch 'ocr-perf-integration' into work
- asset-ai: the live anchor can follow the trip, and text leaves the 5090
- asset-ai: the camera moves the world, and the world starts still
- asset-import: the EA strategy classics, in the one 2D contract
- rtsmap: one seeded generator for tiled strategy maps
- asset-ui: one card for the strategy classics, with a pack dropdown
- asset-ai: music3 reference-audio path, ocr/h3 backends, registry
- asset: mp4 sample index for range-streaming, chat tools, import profiles
- cnc: tiberium is twelve growth frames, not twelve empty variants
- platform: native file and save dialogs, in-house on all three desktops
- chat: the scan holds out for a lane home
- chat: a full home queues you — take the free lane
- chat: the preload has a percentage, and the boundless cap stops showing
- llm cuda: the 32x2 attention tile — even GQA ratios stay on MMA
- sa3 gets a bake path: the sfx model's tables precomputed by a diffusion-side bin
- sqlite_query: anti-join regression test
- td import: HARV's second frame block is its harvesting cycle, not a turret
- asset-ui: sprite enhancement runs on the 32B dev DiT — distillation, not the prompt, was the ceiling
- ai-hub: makepad-asset-ai becomes makepad-ai-hub at libs/ai/hub, the chat pane becomes makepad-chat-ui, the service bin
- asset-ui: test health fixtures grow the realtime field they were born without
- ai-hub: one home at ~/.makepad — weights/ run/ cache/ logs/, the service cache migrates from ai_content by a single re
- ai-hub: subprocess workers die with the node — process groups everywhere, PDEATHSIG on linux, one KILL_ON_JOB_CLOSE Jo
- ai-hub: the hub object — AiHub::in_process, pipes vocabulary, and the local LLM engine generalized out of mpfiles (aic
- strict-json: the dependency-free JSON module gets its own crate; asset-client re-exports it so nothing downstream move
- ai-hub: the machine layer — node entries, the 0600 machine token, and the residency election that IS the lock (aicore
- ai-hub: MPHUB1 — the fabric beacon only dedicated nodes can send (aicore §4)
- ai-hub: job leases — work lives only while it is renewed (aicore §8)
- asset-creator: the pipeline library is born — specs, the deps gate, and the derived-state law (aicore §9)
- ai-hub: RAM residency facts — the CPU-side twin of residency.rs (aicore §3)
- ai-hub: ETA placement primitives — relative GPU throughput, the four-term estimate, and an observable breakdown (aicor
- ai-hub: leases go live on the wire — origin fields on submit, /job/<id>/keepalive, /bye, and the reaper that cancels w
- ai-hub: the chat providers move in — fleet qwen, openai, grok, claude/codex/grok CLIs, the responses driver, and the w
- asset-creator: the engine — one pipeline run against the hub, deps-gated, spliced, cancellable, resumable-by-construct
- ai-hub: the machine node mode — --machine binds loopback, registers in ~/.makepad/run, and exits on its own once idle
- asset-creator: makepad-creator-run — the detached client for runs that must outlive a window (aicore §9)
- ai-hub: a native Claude Messages-API provider — API-key or Claude Code OAuth, bounded SSE streaming, injected tools (a
- route + converse: off makepad_ai — the Agent seam moves to converse, route's cloud dispatcher rides the hub's Claude p
- asset-creator: the preset tables move in — fifteen chain-policy constants shared by every creator app (aicore §9 / P6)
- makepad_ai is deleted — every backend is a hub pipe, the agent seam lives with its consumers (aicore §14, decided 2026
- ai-hub: loads hold the machine residency election — set_model_state claims on Loaded and publishes the service port (a
- ai-hub: chats run the machine election — route to a serving holder, wait on a loading one, claim and publish when open
- ai-hub: pick_for_domain_eta — ETA-ranked placement over the shared hard-filter core (aicore §6 / P4)
- asset-creator: the engine picks a provider per stage at dispatch time — a chain's later stages see fresh fleet state (
- ai-hub: the fabric secret gates the service HTTP surface — bearer on everything but /health and the ticketed peer path
- vj: DREAM runs execute in the app — pipelines.rs becomes the run it used to watch (aicore §9 / F1)
- asset-creator: the runner — generate one thing and put it in the catalog, one implementation for every surface (aicore
- chat-ui: the session runs in the app — no broker anywhere on the chat path (aicore P8 / F5)
- asset-store: assets.query is a first-class query endpoint — the bounded SQL surface outlives the broker (aicore P8 / F
- asset-creator: CreatorTools — the chat tool pack for a store that only stores (aicore §9 / P8)
- asset-store: the shrink — the store stores (aicore P7)
- importer + asset-server host: the coordination era ends (aicore P7)
- store config purge + asset-ui goes fleet-direct; the derive protocol gets its route proof (aicore P7)
- client + chat dispatcher: the dead wire comes out (aicore P7/P8)
- ai-hub: 0.3.0 — the health version says which era a node runs
- ai-hub: the default fleet is 'gen' — apps hear the LAN without env plumbing
- ai-hub: the preload note percents the prefill, not the job bar
- ai-hub: conversations keep their KV — the wire mirror, the lane identity, the in-turn dynamic context (aicore §7)
- ai-hub: an open-think model is thinking from its first token
- libs: the zero-warning sweep — stitch casts say what they mean, xatlas keeps upstream's surface quietly
- zero-warning sweep, round two — the first full-workspace pass
- zero-warning sweep, round three — the model lanes and the deep examples
- zero-warning sweep, round four — the last stragglers
- zero-warning sweep, round five — vj and chat-ui
- zero-warning sweep, round six — three cascades

Co-authored-by: Claude <info@makepad.nl>
2026-09-01 16:46:31 +02:00
Admin
4ddba62d4b engine libs: csg document, stitch simd, sim providers, teamtalk LAN voice, gltf writer, render, route and maps, bench hygiene
Squashed from work; the fine-grained history is under tag archive/work-2026-08-29:
- mp* wave: mpwm window manager + the mp app family, WM API, theme bridge, PDF engine fix
- mpwm polish wave: terminal key focus, focus-history close order, pop-back-to-origin, occupied-workspace cycling, demo
- work: land the sources the last commits reference
- route: the assistant icon the committed UI references
- fast_inflate: the benches name their dev-deps
- gif/weezl: drop the vendored benches nobody can run
- weezl: the decode tests generate their own LZW fixture
2026-08-29 09:26:27 +02:00
Admin
d25cce9ae6 mp*: the window-manager app family — quick-look, warm-instance pool, pty teardown, theme bridge
Squashed from work; the fine-grained history is under tag archive/work-2026-08-29:
- mp* wave: mpwm window manager + the mp app family, WM API, theme bridge, PDF engine fix
- mpwm polish wave: terminal key focus, focus-history close order, pop-back-to-origin, occupied-workspace cycling, demo
- mpwm: ghost-launch/menu-flash root cause, scroll z-gate, browser binds, path-free status, viewers delisted, Fab opens
- mpwm: Hyprland-exact close animation over a frozen snapshot
- mpwm: warm-instance pool, flat-luminance opens, flicker-free CEF resize
- work: land the sources the last commits reference
- mpwm: quick-look gap fixes; image cache eviction on preview unload
- tweaker: material thumbnails + vibecode popup + ctrl-space notes, undo/redo over the edit ledger, capture-semantics pi
- tweaker: tabbed side panel (Props/Shader/Tree) - shader tab with checkerboard material well + prompt, complete widget-
- mpwm+mpterm: text quick-look — pty teardown deadlock fix, in-place retarget verified, debug probes stripped
2026-08-29 09:26:26 +02:00
Admin
19097335b6 vj: the responsive DJ console (PR #1199 by vjroger), the DREAM expand-flux-video pipeline as one store graph, 16:9 image particles, XF defaults
Squashed from work; the fine-grained history is under tag archive/work-2026-08-29:
- mp* wave: mpwm window manager + the mp app family, WM API, theme bridge, PDF engine fix
- mpwm polish wave: terminal key focus, focus-history close order, pop-back-to-origin, occupied-workspace cycling, demo
- work: land the sources the last commits reference
- vj: responsive DJ mixer + Windows drag-and-drop, cherry-picked from PR #1199 (vjroger)
- vj particles: image mode homes on a 16:9 picture plane
- vj: DREAM pipeline — expand -> flux1 still -> minimax i2v loop, with a canvas picker
- vj: the row shows the real prompt, a flux picker, XF default, and new clips stop vanishing
- a DREAM run is one declared graph the store runs without us
2026-08-29 09:26:26 +02:00
Admin
d24cbee60d asset platform: store pipelines with honest banded progress, vision annotation through the job queue, fleet scheduling (roles, spread, peer provisioning), the music contract, synonym search, the runs card
Squashed from work; the fine-grained history is under tag archive/work-2026-08-29:
- mp* wave: mpwm window manager + the mp app family, WM API, theme bridge, PDF engine fix
- work: land the sources the last commits reference
- kenney: catalogue all 50 free 3D kits; Modal dismissed() never fired
- asset store: central vision-annotation queue; Kenney donate prompt
- asset-ai: vision domain — image + prompt -> text on every fleet node
- annotation runs the fleet's vision services through the normal job queue
- vj: responsive DJ mixer + Windows drag-and-drop, cherry-picked from PR #1199 (vjroger)
- store search: WordNet synonym expansion, query-side; per-term seeks
- video scheduling: a cold model pin never downloads past a warm one, and a dedicated box only serves its role
- an expansion can never lose a run — and `expand: true` finally means something
- fleet panel: a slow box is not a missing box
- h3: first+last keyframe conditioning (the weights were always FL2VA)
- registry: the three H3 FL2VA tiers name their real conditioning
- fleet scheduling: spread before stacking, and a stuck job moves
- a download never steals a job from a box that has the weights
- the faster GPU takes the tie: 6000 > 5090 > 4090
- an evicted flux model gives the card its VRAM back
- a job now records what each of its stages was handed
- open a stage in RUNS and read what it sent
- every expanded song was an instrumental: the lyrics had nowhere to go
- the writer never overwrites words the person wrote
- store: a dependent job's body is spliced from its deps' results at claim
- a fleet box's job row says what that job was asked for
- the store runs a whole pipeline, and one record says how far it got
- a client can declare a whole run, watch it, and stop it
- 100% now means published, and the expander is a job you can queue
- one card says what a spawned task is doing, everywhere
- a run that ends says so, instead of being noticed later
- when every box holding the model is busy, buy another copy
2026-08-29 09:26:25 +02:00
Admin
6c8d301da9 asset-ui: the classic import surface follows what the importer now produces, and the chat tells the truth while it works
Squashed from work; the fine-grained history is under tag archive/work-2026-08-26:
- vj: the thumbnail pipeline becomes one honest machine, and effects go livecodable
- asset-ai: the chat tells the truth while it works
- audio: a FLAC decoder from the specification, beside the MP3 and Vorbis ones
- asset-ui: the classic import surface follows what the importer now produces
2026-08-26 08:49:50 +02:00
Admin
4c5228d9ca mixer: a live LR-Mix surface for the XR18
Squashed from work; the fine-grained history is under tag archive/work-2026-08-26:
- mixer: a live LR-Mix surface for the XR18 — strips paired the way the desk is run, auto-connect, EQ that bends its own curve, and a sweep paced so the console drops nothing
2026-08-26 08:49:49 +02:00
Admin
705d6c73ed vj: the console — real transports and reverse, the music explorer's IMPORT, local store, lyrics and model plumbing, DJ-tab scroll knobs, title-click reset, crossfade fix, stem headroom, and music/sfx browse models
Squashed from work; the fine-grained history is under tag archive/work-2026-08-26:
- vj: the music explorer gets IMPORT, and catalog controls fold away in local mode
- vjfx: three lanes land — engine hooks + hold stage, the videomesh engine, and the audio picture
- vj: the thumbnail pipeline becomes one honest machine, and effects go livecodable
- vj: thumbnails become mp4 — hardware-coded sheets at measured-4K cells, and the bake stops racing the GPU
- store: the ceremony dies — batch publish, one transaction, and the engine stops re-reading its own log
- store: the ceremony dies — batch publish, one transaction, and the engine stops re-reading its own log
- vj: the console grows real transports, and the deck stops lying about reverse
- vj: reverse earns a memory, and the effects stop aging
- vj: video goes NV12 end to end, and the GPU does the unpacking
- vj: windows hands the main thread one megabyte, and the script tree wants two
- vj: the GPU learns to see motion — realtime frame tweening on every deck
- vj: the tweener learns — RIFE runs on the Mac and feeds the warp
- vj: the classical tweener grows up, and every deck gets a tween chip
- models: an interrupted install can never load broken
- vj: the transport becomes a platter — velocity in, position out, one map
- vj: the tween presenter reads the platter — one clock per deck, cued once at the frame on screen
- vj: the presenter switch lands without its scaffolding
- vj: the next pair's fields are fetched ahead of the change under the capacity law — a pair change costs an ordinary beat; macos: the layer's own display link paces the frame when the system offers it, the old path stays as fallback
- vj: AI3 subdivides adaptively — one, three or seven neural frames per pair, chosen from measured synth time against the pair's own period, with classical flow between them and a 7-3-1-FL fallback; the deck shows the depth
- vj: local store, lyrics and model plumbing, and the frame-interpolator's device parity check
- vj: DJ-tab scroll knobs, title-click reset, crossfade fix, stem headroom — plus the pre-Ampere CUDA fix (#1193)
- vj: the deck explorer lists music-tagged audio, the sfx surface filters by tag not category, the track list fills the window, and the modal host has no layout footprint

Co-authored-by: Rogier de Leeuw <vjroger@gmail.com>
2026-08-26 08:49:49 +02:00
Admin
dc389d71a1 vj: the transport becomes a platter — one clock per deck, the producer contract, GPU frame tweening with RIFE, NV12 end to end, AI3 adaptive subdivision, and bit-identical decks
Squashed from work; the fine-grained history is under tag archive/work-2026-08-26:
- vj: thumbnails become mp4 — hardware-coded sheets at measured-4K cells, and the bake stops racing the GPU
- vj: the console grows real transports, and the deck stops lying about reverse
- vj: reverse earns a memory, and the effects stop aging
- vj: video goes NV12 end to end, and the GPU does the unpacking
- vj: the GPU learns to see motion — realtime frame tweening on every deck
- vj: the tweener learns — RIFE runs on the Mac and feeds the warp
- vj: the classical tweener grows up, and every deck gets a tween chip
- vj: the tween clock tells presented time, not producer time
- vj: the transport becomes a platter — velocity in, position out, one map
- vj: the tween presenter reads the platter — one clock per deck, cued once at the frame on screen
- vj: the OFF tier joins the platter — a resident clip's picture is cache[nearest(pos)]
- vj: the media thread loses its second clock — resident clips park the decoder
- vj: the producer gets a contract — keyed ladders, deadlines, and a capacity law
- vj: two decks, one law — identical inputs are bit-identical, and the warp agrees to the byte
- vj: the presenter switch lands without its scaffolding
- vj: the next pair's fields are fetched ahead of the change under the capacity law — a pair change costs an ordinary beat; macos: the layer's own display link paces the frame when the system offers it, the old path stays as fallback
- vj: AI3 subdivides adaptively — one, three or seven neural frames per pair, chosen from measured synth time against the pair's own period, with classical flow between them and a 7-3-1-FL fallback; the deck shows the depth
- video_flow: the flow debug bins, declared behind the convert feature so --no-default-features skips them instead of failing
2026-08-26 08:49:48 +02:00
Admin
cb49b032df vjfx: 104 presets, engine hooks + hold stage, the videomesh engine, the audio picture, livecodable effects, and mp4 thumbnail sheets
Squashed from work; the fine-grained history is under tag archive/work-2026-08-26:
- vjfx: 104 new presets — the transition lane fills out and the screen family goes wide
- vjfx: three lanes land — engine hooks + hold stage, the videomesh engine, and the audio picture
- vj: the thumbnail pipeline becomes one honest machine, and effects go livecodable
- vj: thumbnails become mp4 — hardware-coded sheets at measured-4K cells, and the bake stops racing the GPU
- store: the ceremony dies — batch publish, one transaction, and the engine stops re-reading its own log
- store: the ceremony dies — batch publish, one transaction, and the engine stops re-reading its own log
- vj: the console grows real transports, and the deck stops lying about reverse
- vj: reverse earns a memory, and the effects stop aging
- fab: a 3D creation shell and the viewer built on it
2026-08-26 08:49:48 +02:00
Admin
9821263b2c fab: a 3D creation shell and the viewer built on it — colour picker, material textures, dials that move the scene while they drag, the glTF loader, the tour, and the probes
Squashed from work; the fine-grained history is under tag archive/work-2026-08-26:
- fab: a 3D creation shell and the viewer built on it
- raytrace: the traced pane starts coarse and doubles to native, with the raster underneath
- fab: a colour picker, a material's textures, and dials that move the scene while they drag
- texcomp: the block codec and the container every texture will travel in
- fab: FAB_PROBE_MAT — per-material triangle counts, texture presence and uv spread in the roof probe
2026-08-26 08:49:47 +02:00
Admin
152b11f20a vj: the deck models install themselves, and Windows gets its waveform back
- INSTALL MODELS under the music explorer: a download dialog naming both
  MIT weight sets (BS-RoFormer splitter 527MB, whisper large-v3-turbo
  1.6GB), where they land and their licenses; resumable sha256-pinned
  downloads through the asset-ai downloader (featureless dep — the same
  slice the asset UI links); cancel mid-flight (the button flips to
  CANCEL, .part resumes later), MB progress, and the row disappears on a
  provisioned machine. When the last model lands the loaded decks
  separate immediately: the stems worker now re-probes the checkpoint
  per job instead of latching its absence, and the lyrics transcriber
  unlatches too (the Apple fallback yields to whisper mid-session).
- DrawWaveLane's stem palette moves from instance inputs to uniforms:
  36 vertex inputs blew D3D11's vs_5_0 limit of 32 (error X4506), which
  left the music decks with NO waveform at all on Windows.
- --remote HOST:PORT binds a named interface so another machine can
  drive an app over the LAN (fleet-box testing); bare --remote stays
  loopback.
- queue chip: the + glyph centres in its 26x18 box.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-23 10:16:08 +02:00
Admin
f44616b2a7 makepad-vj effects: the effect renderstack, with the shaders inside the documents
The VJ's EFFECT surface is a small renderer of its own. An effect is a
`.splash` DOCUMENT — engine choice, stages, fields, parameters and now the
SHADERS THEMSELVES live in the document rather than in Rust. That is the
shape this commit introduces (dev has never seen an intermediate one): a
document is the whole effect, and the Rust side is the engine families that
documents draw with.

The engine families: particles and emitters, GPU sim swarms and fluid, static
meshes (firefly synchrony, harmonograph loom, domino liturgy), tiles, flock,
clouds, city, pipes, stock charts, an SDF raymarcher with a subclassable
`scene_sdf`, and a mountain-jet endless range with a beat-pulsed fighter.

Three things make them a system rather than a demo reel:

  - SIM FIELDS — a float simulation-texture primitive, so an engine can carry
    GPU state across frames (wind, particles, fluid) instead of being a pure
    function of the clock.
  - CONTENT COUPLING — `content:` in a document and `input0` on every engine's
    tex0, so an effect can consume the program video: drapes, backdrops,
    mirrors, billboards, chrome, frescoes, canopy, glass, silk, wall, swarm,
    pen and mosaic families all take the picture playing behind them.
  - MUSIC BINDING — shaders read the beat, so the whole library moves with the
    track rather than on its own clock.

Roughly a hundred seeded preset documents ship with it, each with a lazily
rendered animated thumbnail (rendered 4x and box-downscaled, 16-tap SSAA);
the thumbnail pass went from four minutes to eleven seconds. CONTRACT.md is
the document contract and its verify recipe; IDEAS.md is the campaign tracker.
2026-08-23 01:35:44 +02:00
Admin
17b8a658fc makepad-vj: the live performance console — sweep-law transport, slots, and VFR import
A VJ console that is its own asset store: five surfaces (VIDEO program tiles,
MUSIC DJ decks, SFX, EFFECT slots, LIGHTS) over the asset server, driven by
APC40 hardware or the screen.

The transport is the centre of it. The SWEEP LAW: one direction, one beat
step, and a pacer that never hiccups — a loop wrap is never fast-forwarded and
the grid keeps no holes. On top of that: bracket-to-bracket scrub math so the
playhead learns the user's trim, SCRATCH as a sprung shuttle on every deck
transport, ping-pong and per-slot mute, hot-standby decks, one rate authority,
and beat sync that fits a whole clip into N bars at a musical rate while
cached loops keep real wrapping pts so the phase survives.

VARIABLE FRAMERATE VIDEO IMPORT measures the flow field a clip needs without a
model (libs/video_flow), which is what makes free-rate bounce-looping GPU
playback possible; the enhance pipe uprezes and tweens a deck clip in one
decode and one encode, with the motion vectors inside the mp4.

The console itself: makepad orange and the brand lockup, Blender-style BPM,
system tooltips, popover sliders, bracket trim handles, one button family and
a no-push band, doc-labeled slot dials with MIDI learn and strict typing, a
BEATS dropdown and slow-biased jog, an IMPORT panel that arms, runs and stops,
and BLAST — one press invents a visual for every parallel pipe. Generation
runs six jobs in flight across the fleet, and a job row names what it made.

The library grid fills as a relay — page, detail, manifest, blob, decode,
texture — and every hand-off used to be picked up by the 20Hz poll timer, so
each hop cost a tick however fast the store answered. The hand-offs now run on
the frame while anything is owed, manifests jump the queue like the details
that produced them, and resolve/decode/worker width all widen while nobody is
on stage and narrow together the moment the program window opens or a deck
plays. The visible page resolves first. Measured on the same warm cache: a
48-tile page 4.7s -> 1.9s, a tab's first visit 4.3s -> 1.1s, decode wait 48ms
-> 9ms, a second visit painted in the same frame with zero decodes, and no
UI hitches across the session.

Also here: wave analysis (tempo, grid, downbeat, tiles) with a judge suite
that scores the detector against click tracks and a human drummer, stem
separation, karaoke lyric display, Art-Net/DMX light control, and an output
window that projects without janking on Windows mouse movement.

466 tests, zero warnings.
2026-08-23 01:35:44 +02:00
Admin
9d2e17e3b9 chore: README, workspace, agent docs, and the box-driving scripts
- README: a build quick-start for macOS and Windows, the honest Linux
    story, what CUDA is for and how to install the separation model.
  - AGENTS.md: the `--remote` control surface protocol, so the harness it
    documents is usable without reading platform/src/remote.rs.
  - Cargo.toml: workspace membership for the crates this series adds and
    removes.
  - tools/: the Windows box scripts (wincmd, winps and friends, winrun) and
    remote_smoke.sh — how a build gets driven on a remote machine.
  - apps/asset-server: the standalone server binary and its README.
  - Small follow-ups in libs/{windows,apple_sys,makepad_test,mbtile_reader,
    converse} and apps/route, plus .gitignore and makepad.splash.
2026-08-23 01:34:36 +02:00
Admin
e2616d2d45 asset-ui: analysis, masks, music, webcam, and a library that keeps up with the store
The asset browser becomes the front end for everything the AI stack can do:

  - analysis — a content analysis pass over imported assets.
  - mask_paint — paint a mask over an image for inpainting.
  - music_page — the music generation surface.
  - webcam — live capture as a generation input.
  - fast_presets, store_content — preset and store-content plumbing.

Around them the existing surfaces get the corrections the store and the video
widget forced: sprite-split and child-pass thumbnail fixes, one video widget
everywhere (opens stop fighting each other and the rail never reflows),
grouped classic/Duke import that lands 102 assets instead of a card flood,
chat that greets with silence rather than a banner, and a pipeline that
survives restarts.
2026-08-23 01:34:36 +02:00
Admin
6d708d9391 Clean the public tree for a checkout other people can use.
Drop the archived old/ tree, root AI notes, splashgame.md (Arcade lives
in the private sandbox), widgets-dll leftovers, and LAN/oracle helper
scripts. Move download_*.sh into tools/, and move sim/math out of
libs/game/ so the public repo no longer has a game/ directory.

Also quiet first-party compile noise and skip asset-ui tests that need
sandbox kits or uncommitted fixtures.
2026-08-18 15:23:19 +02:00
Admin
f0e1f83831 Close the rik2 land: workspace, splash, and sandbox cleanup.
Point the workspace at libs/asset and libs/render. Drop Arcade
frontend, gamemaker, rig, pack registry, physics, and the old
libs/game gameplay crates. Those live in the private sandbox repo.
2026-08-18 14:24:00 +02:00
Admin
bdc7c0a227 Land Asset UI and VJ from rik2.
Catalog/import/viewers and the live performance app. They use
libs/render preview play, not the Arcade sim crate as a dep.
2026-08-18 14:24:00 +02:00
Admin
0b1249376e Arcade: the demo world becomes a splash template
The directive was that the demo scene become a project template you can make
new worlds from and edit by voice. This is the blocker gone: an authored
splash file can now build the world the demo builds.

`apps/arcade/resources/template.splash` is that world — sky, sun, terrain,
crossroads, two rows of houses, six distinct drivable cars, a bearded hero,
wandering townsfolk and the spiral climb — entirely in `game.*` verbs. It
loads: "eval ok, 28 entities, 5 skinned characters".

Two engine gaps had to close first, and both were the same shape: arcade
owned something the script could not reach.

**The script could not see the asset library.** `ScriptHost::set_assets`
existed and arcade never called it, so every asset verb — find_model, model,
kits, cast — reported "no stock library on this device" on a machine with
4,700 models on disk. arcade built an index for its own Rust demo and kept
it. An authored game therefore could not use any of the art the demo is made
of, which is most of the reason the demo had to stay in Rust. The index is
now built once, lazily, and shared with the host.

**Script characters had no bodies.** The skinned draw path walks
`self.villagers`, which only the built-in Rust world ever filled, so every
character in an authored game rendered as its bare collision box — which is
what a splash world looked like next to the demo. `sync_script_characters`
now builds that list from `blocks.characters` after each eval, matching the
model id the script asked for against the loaded cast and round-robining on a
miss. A wrong-looking character beats an invisible one.

Not yet done, and the template is honest about which parts are which: the
climb's moving lifts and the fireworks still have no verb, and the world is
written out longhand where the Rust version used loops.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 16:59:46 +02:00
Admin
6320c0bc68 Script: game.terrain uses the real generator; cars can carry a model
Groundwork for the demo becoming a splash TEMPLATE. Two things the script
surface could not express, so the demo had to stay in Rust.

**`game.terrain` carried its own single-octave value noise.** That meant the
terrain an AI could reach from splash was strictly worse than the terrain the
engine could make, and the two drifted independently — every fix to
`libs/game/gen/terrain.rs` (fBm, domain warp, world-unit frequency,
slope-aware colour, rim relief) was invisible to any authored game. It now
calls that generator. One generator, one set of bugs.

New params exposed: `feature` (distance between hills, in world units —
answerable, unlike "what is a good freq"), `octaves`, `warp`, `ridged`,
`flatten`, `rim`/`rim_start`.

Two compatibility decisions worth stating:

- `freq` still works. It meant cycles per CELL INDEX, so its wavelength in
  world units is span/((cells-1)*freq); translating rather than ignoring it
  keeps an existing world looking like itself.
- `step` now defaults to 0, not 1.0. The old default quantised every smooth
  slope into one-unit stairs, so a script asking for smooth terrain got a
  contour map. Scripts that want terraces still ask for them.

**`Car` now carries a model**, as `Character` already did, and `game.car`
takes `{model}`. Without it a host could only ever draw ONE kind of car —
which is exactly why the arcade fleet had to live in Rust, and why a
splash-authored world could not have more than a single vehicle shape.

The renderer prefers the car block's own model and falls back to walking the
`parked_car` role, so the same code path serves an authored game and the
built-in demo without the script needing to know the fallback exists.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 16:59:46 +02:00
Admin
67784be752 Town: a drivable fleet, two rows of houses, and a spiral climb
**Every car drives.** Six vehicles — ambulance, delivery, police, sedan,
taxi, delivery-flat — each a real chassis with a Car block and its own model,
so any can be walked up to, got into and driven away.

The logic is shared by construction rather than by copying, which is why one
car became six with no new plumbing: the interact prompt is DERIVED from the
presence of a car block, the mount is derived from the prompt, and the
renderer now walks every entity tagged "car" instead of finding the first.

Two asset-query lessons, both silent failures:

- `Spread::Variants` means "all from ONE family". Right for a terrace of
  houses, exactly wrong for cars — it returned six ambulances. `Spread::Kinds`
  caps at one model per family, which is what "one of each type" means. The
  query also has to name what the models are CALLED: "car vehicle sedan truck
  van" matched a single model.
- Asking for more house variants than the kit has houses returns LOT pieces.
  `building-type-o` is a swimming pool, and it duly appeared on the street at
  house height. A pool is a valid model that scales like any other, so
  nothing warns you.

The chassis now spawns `hidden` rather than being hidden later once a model
loads. That ordering made "draw a red slab inside the car" the default and
correctness an afterthought — with one car the afterthought fired, with six
it hid the first and left five driving inside red boxes.

That is a workaround, not a fix: `hidden` exists because an Entity is both a
physics body and a renderable, so every collision shape must opt OUT of being
drawn. A body should not be renderable at all.

The climb replaces the linear jump course: nine ledges spiralling a solid
tower, two timing platforms, an orbiting lift, a summit. Rise per ledge stays
inside one jump, the route is visible from the bottom, and falling costs
height rather than a life.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 16:59:45 +02:00
Admin
14fb97e500 route: AI agent on by default again — perf campaign over; MAKEPAD_NO_AI=1 is the profiling opt-out
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-05 16:59:45 +02:00
Admin
8d32736471 Fireworks: real shell scale, stars that drift instead of plummet, shell styles
"They don't fall this quickly." Correct, and the model was wrong rather than
mistuned: the fall term was 0.5*g*t^2, free-fall, as if each star were a
dropped rock. A star is a few grams of burning composition with a lot of
drag, so it reaches terminal velocity almost immediately and then DRIFTS.
Vertical motion is now quadratic for the first instant and a constant ~7 m/s
descent after — which is the hang every display has and ours did not.

Sized against reality, which our world happens to make easy: one unit is one
metre here (a character is 1.8 tall, houses 6-8). Real stars leave the burst
charge at 50-100 m/s and drag stops them in about a second, so the break
opens to speed/k across. At k = 3.08 the new 48-80 m/s gives a 30-52 m
diameter shell — a 3in to 6in break, what a town display actually fires. It
was 11-17 m before, which is why it read as a firecracker. Bursts moved up to
38-58 m accordingly; a real 3in reaches ~80 m, but ours stay lower so they
sit inside a camera that is pitched down at a street.

Shells are no longer all the same. A third are DUAL-COLOUR breaks, where half
the stars carry the second colour from the start rather than merely cooling
into it — the two-tone shell in every display photo. The split is per star and
stable, so a ray keeps its colour all the way out instead of shimmering. A
sixth are WILLOWS: stars thrown at half speed with a heavier drift, so they
arc over and trail down. The rest are plain peonies.

Style is chosen on the CPU, one float per shell, and applied entirely in the
splash-side `spark_color` — the engine still knows nothing about how any of
this looks.

Also dropped the leading-edge brightening: with uniform star speed there is no
longer a fast outer shell to distinguish, so it was tinting at random.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 16:59:45 +02:00
Admin
2a42000f36 Fireworks: streaks are trains of dots, not stretched rects; sound removed
"Fireworks are streaks of PARTICLES, not a sphere expanded set of rects" —
and the reference photos show it plainly: every ray is beaded, a string of
glowing points strung along the path its star has flown.

So the model changed rather than the tuning. Each star is now a TRAIN of 8
beads, and a bead is simply that star's own closed form evaluated 40ms
earlier. Nothing extra is simulated and nothing extra is uploaded — the
trajectory was always a function of time, so sampling it at t - delay is free.
512 beads per shell is 64 stars with a real trail each.

Beads taper and dim toward the tail, so a ray has a bright head fading back
toward the burst centre, which is the shape every photograph shows.

I had built this as a stretched quad first — elongating the billboard along
the screen projection of the velocity. It is the standard trick and it is
wrong here: it draws one long rect per star, so the rays are smooth bars
rather than beaded, and a rect wide enough to see is also wide enough to look
like a slug. Removed.

The sprite is a round, ANTIALIASED dot. `smoothstep` rather than a linear
ramp, because a hard cutoff shows the rasteriser's stair edge on something
this small and bright, which is exactly where aliasing is most visible. Both
falloff terms reach zero at 0.8 of the half-width — inside the corners as well
as the edges — so the billboard border can never cut the dot. That was why
every spark read as a filled square.

Sound removed entirely, including the synth preset.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 16:59:45 +02:00
Admin
1509190fc0 Fireworks: a real peony — even star spacing, uniform speed, quieter bang
"Still a bit too random." It was, and the fix corrects something I took from
the wrong source last time.

Looked up how actual shells are built. Stars are packed EVENLY around the
burst charge and lit at the same instant, so they all leave with the same
force — that even spacing is precisely why a peony reads as round from every
angle. Two changes follow:

- Directions come from a FIBONACCI SPHERE instead of a per-spark hash. The
  golden angle steps phi so successive stars never line up, and z steps
  linearly so they spread evenly in AREA rather than in latitude (which
  bunches them at the poles). Hashed directions give clumps and holes, and no
  amount of extra sparks makes that look like anything but noise. A per-shell
  rotation keeps two shells from being the same object twice.
- Speed is near-uniform (6% jitter) instead of a 4:1 spread. I took that
  spread from the canvas demos last commit — but random(1,10) is a 2D trick
  for filling a disc. In 3D, identical stars igniting together travel
  together, and the spread just turns the sphere to mush.

The bang was also wrong: 900Hz of broadband noise at 0.34 gain is a shotgun
in a small room. A shell is heard from far away, so it arrives mostly low and
quiet — now a 220->38Hz thump at 0.085, falling away over 1.1s.

Sources: epicfireworks.com "The Art and Science of the Chrysanthemum Firework
Effect", liuyangfireworks.net "Ball Shell vs. Cylinder Shell"

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 16:59:45 +02:00
Admin
f7aa5f8297 Fireworks: symmetric bursts, longer and closer, with a bang
Reported as "too squiggly — real fireworks are more symmetric, they don't
move in big waves". They were right, and the cause was mine: the arcade
style's `spark_motion` added a curl and a jitter, so every spark travelled a
visible wave.

Read how the canvas demos actually do it (CreativeJS, the codepen/thecodeplayer
lineage). The answer is that they apply NO positional noise at all: one
uniform radial angle per spark, then nothing but friction and gravity. All
the shape comes from the SPEED spread, not from moving sparks around. Three
changes follow from that:

- `spark_motion` returns zero. The hook stays, because it is the right seam
  for a style that wants to be strange — a spiral shell, a jellyfish — but
  the default is symmetric.
- Drag matched to the convention: `speed *= 0.95` every frame at 60fps is
  exactly e^(-kt) with k = -60*ln(0.95) = 3.08, replacing a softer constant
  I had guessed.
- A 4:1 speed spread instead of 1.8:1. The demos use random(1,10); a narrow
  spread leaves a hollow shell with nothing in the middle.

Also reported: too fast and too far. Shells now live 3.2-4.6s (was 1.5-2.4),
throw sparks 17-27 units (was 11-19), and burst in a 25-46 unit annulus (was
out to 68).

The sprite is fully contained inside its quad. Its falloff dies at 0.8 of the
half-width, inside the corners as well as the edges, so the billboard's
straight edge can never cut the glow — which showed as square-clipped sparks.
The old cross-flare ran to the border and was the worst offender, so it is
gone. Colour is emitted unpremultiplied with zero alpha: pure additive light
under premultiplied blending, so sparks add and never occlude.

And they bang now. A shell reports its burst point exactly once, when its age
crosses zero, and the host plays a broadband noise burst there — positioned,
so it pans and attenuates like any other world sound. Noise rather than a
tone because a shell is broadband; a tone reads as a laser.

Sources: creativejs.com/tutorials/creating-fireworks,
thecodeplayer.com/walkthrough/canvas-fireworks-tutorial

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 16:59:45 +02:00
Admin
362c96d63b Fireworks: parametric GPU sparks with a splash-side style seam (gated off)
CPU launches shells; the GPU animates every spark from a closed form. Once
a shell bursts nothing in the world can influence a spark — it flies from a
known point, at a known time, along a fixed arc — so position is a function
of (spark index, seed, age) and there is no state to step and nothing to
upload per frame. Twelve shells of 320 sparks is 3,840 particles for TWELVE
instances of CPU work, against 3,840 simulation updates and a 3,840-instance
upload for the stepped path.

**The style seam is the point.** Rust owns structure — trajectory, lifetime,
billboarding — and exposes four hooks a splash script overrides by
inheritance: `spark_motion` (swirl, fizzle, drift), `spark_size`,
`spark_color`, and `spark_pixel` (the sprite program). Arcade's own styling
lives in its `script_mod!`, not in Rust: a three-stage temperature burn,
hotter on the fast outer shell than the slow core, with sparse glitter
strobing. A generated game can restyle the sky without being able to reach
the simulation.

Two real bugs fixed on the way:

- **Vec3f instance fields silently misalign.** A `Vec3f` is tightly packed in
  Rust but a `vec3` obeys 16-byte alignment in the shader ABI, so the burst
  origin read back as zero and every shell rendered at the world origin, on
  the ground. The other shaders here get away with `Vec3f` because theirs are
  `uniform`, not instance. Everything is packed into `vec4`s now, which is
  the shape the hardware wants anyway and removes the class of bug.
- **Eight bare `panic!()`s in the shader backend** now name the backend, the
  stage and the IO type, and the commonest case — reading a geometry
  attribute from `pixel:` — gets a message saying so and showing the varying
  that fixes it. Previously a shader that tripped it aborted with no text at
  all, which is not a barrier, it is a wall in the dark.

**Gated off by default.** The spark SIZE instance is not reaching the shader:
scaling it 25x changes nothing on screen, so every spark draws as a
screen-filling blob and the sky whites out. That is the same class as the
Vec3f bug above and I have not found the second instance of it. The launcher,
the trajectory, the placement annulus and the whole hook surface are finished
and tested; this is one plumbing bug from working. ARCADE_FIREWORKS=1 turns
it on to work on it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 16:59:45 +02:00
Admin
3e7a66cc7d Ramps you can actually walk up, a crossroads, and a jump course
**The ramp was a solid cube.** Reported as "i dont seem to be able to walk
the character up the slanted block towards the platform" — and the wedge
had no collision handling anywhere. Movers sweep against static AABBs, so a
`Shape::Wedge` was collided as the box that CONTAINS it: the ramp built to
be walked and driven up presented a vertical wall at its low edge, and you
stopped dead against nothing you could see.

Wedges are now surfaces rather than walls, handled exactly the way terrain
already was — the symmetry is the point, since terrain had solved this
problem years earlier in the same file. They are excluded from the axis
sweeps, and a ramp floor pass sits underneath: walk up where the slope
rises less than CLIMB, blocked where it rises faster. That falls out
correctly at both ends without special-casing either — the gentle slope is
walkable, and the wedge's full-height back face is still a wall, because
there the surface jumps well past CLIMB in one step.

Sampled across the mover's whole footprint, not just its centre, so
standing with half your feet on a ramp stands you on the ramp.

**Conforming statics to terrain now ADDS the ground height instead of
replacing it.** Replacing looks equivalent, because everything is authored
resting on flat ground — right up until something is deliberately in the
air, at which point it flattens every platform, buried base and raised
ledge onto the dirt, and the failure reads as the level's fault rather than
the function's. Adding is a no-op on flat ground and rides the slope
elsewhere. Found by adding a jump course whose heights it ate.

**A crossroads and a side street.** One straight road reads as a corridor;
a junction is the smallest thing that makes a place feel like it has
somewhere else to be. The side street runs out to the yard, so the physics
corner is somewhere you drive TO rather than somewhere that is merely
nearby.

**A jump course**: a static step to read the route from, a platform that
slides across your path, one that rises and falls, and a wide still ledge
that is obviously the end. Gaps are sized against the controller's actual
jump distance rather than eyeballed, and the two movers run on different
periods so they drift in and out of phase instead of presenting the same
crossing every lap. The ramp's high edge is the run-up, which is what turns
two separate toys into one thing to do.

Three tests, one per claim: a character walks up, the back face still
stops them, and standing on the slope reports on_floor (without which the
controller silently refuses to jump). The first version of the walking test
passed for the wrong reason — its world had no ground, so the walker fell
past the ramp and met it from BELOW, where being blocked is correct.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 16:59:45 +02:00
Admin
3544d92593 Arcade: bearded player, fixed sun, wheels under the wheels
**A guy with a beard.** The player wears the barbarian, pinned BY NAME
rather than by cast index — the cast is assembled from whatever the asset
library contains, so an index silently becomes a different person the
moment a pack is added, and that failure reads as a cosmetic surprise
rather than a bug.

Kenney was the obvious cheaper choice and it does not work: every character
GLB in both Kenney packs has only root/torso/head/arm/leg parts, and their
colormap is flat colour swatches with no painted faces, so there is no
bearded Kenney character to pick. Verified by rendering the heads rather
than by guessing from filenames.

**The sun is fixed.** It swept a full day every 40 seconds, which looked
lively for about ten seconds and then just cost money: AO and cast shadows
are baked, and the baker rebakes whenever the sun crosses
`sun_rebake_angle`, so nothing on screen ever settled. Now one explicit
direction at 38 degrees elevation — the engine default sits at 54, nearly
overhead, where shadows barely clear their own footprint and nothing reads
as standing on anything. At 38 a shadow runs about 1.3x its caster's
height: long enough to describe the shape and show the ground's slope,
short enough that the village does not vanish into its own shade. The day
cycle survives behind ARCADE_DAYCYCLE, where it belongs until rebaking is
incremental.

**The simulated wheels now sit under the drawn ones.** Reported as the car
"not really following the landscape, wheels not really touching". The
config's track and wheelbase are authored for a generic chassis while the
mesh is scaled from whatever the pack shipped: for the stock truck the
drawn wheels sit 1.30x wider and 1.39x further apart than the simulated
ones, with a 1.33x radius. On flat ground that is invisible, which is
exactly why it survived a flat-ground test measuring a 3e-6 residual — the
model's lowest point still lands on the road. On a slope it is not
invisible: the body pitches and rolls about the SIMULATED contact points,
and a drawn wheel further out swings through a bigger arc, so it lifts off
the ground. Adding terrain is what made it visible.

Fitted from measured bounds at model-load time, the same discipline as
sitting the mesh on measured bounds rather than on the collision box.

I first read "floaty" as suspension bounce and stiffened the springs. That
was the wrong problem; reverted.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 16:59:45 +02:00
Admin
082af7e4d7 Arcade: un-invert the camera stick
Reported as "the camera joystick feels y inverted", and it was.

A pad reports the stick's UP as +y. `look_dy` is in SCREEN units, where y
grows downward and `+look_dy` raises the camera and looks down. The stick
value went in unnegated, so pushing the stick away from you looked down
while pushing the mouse away from you looked up — two devices disagreeing
about which way is up, on the same camera.

The test states the rule as agreement between the devices rather than as a
raw sign. A sign assertion is satisfiable by flipping whichever end of the
chain you happened to be reading, and this chain has three sign conventions
in it (pad, screen, render pitch); what actually has to hold is that the two
physical "push away from me" gestures do the same thing.

`ARCADE_INVERT_Y` still restores the old behaviour, which is now what it
always should have been: a preference, not the default by accident.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 16:59:45 +02:00
Admin
fcfd335f1e Terrain: a real heightfield under the world, and cars that touch the road
**The flat plane is gone.** Terrain existed all along — heightfield collision
via box3d, AO and shadow raymarching against it, a mesh path, a
`game.terrain` verb — and arcade simply spawned a flat slab instead. That is
the fourth capability this week that was built and never called.

Turning it on was not enough, because the generator had faults that a
screenshot explains faster than prose:

- **Single-octave value noise.** Detail at exactly one scale reads as melted
  blobs. Now fBm with domain warp, which is the single highest-value knob
  here: warping the sample point bends contours into ridges and valleys
  instead of round lumps on a visible grid.
- **`step` defaulted to 1.0**, quantising every smooth slope into 1-unit
  stairs. The old default renders as a literal contour map. Off by default.
- **Noise was indexed by CELL INDEX, not world position**, so asking for a
  finer mesh silently generated a different landscape. Resolution should buy
  detail, never a new world.
- **Colour came from height alone**, which paints terrain in horizontal
  stripes like a contour map. Now height AND slope, so rock lands on cliff
  faces and grass on the shelf above them.

Generation moved to `libs/game/gen/terrain.rs` as a pure function. Beyond
testability that was forced: arcade's demo world has no script VM, so the
only generator in the tree was one it could not reach.

`rim_relief` is the load-bearing idea. Terrain interesting everywhere is
terrain you cannot put a town on; terrain flat enough to build on is a green
table. Growing the relief outward gives a playable basin ringed by something
worth looking at, and doubles as a soft boundary. It SCALES the noise rather
than adding a radial ramp — the ramp version has no noise in it and renders
as a smooth machined ring between two flat plains, which I built first and
threw away after looking at it.

Two things the tests taught me rather than confirmed:

- Normalising fBm by the sum of octave amplitudes — the textbook form — makes
  five octaves come out FLATTER than one, because summing decorrelated fields
  concentrates them about the mean. Normalising against the field's own
  extents makes `amp` mean literal relief at any octave count.
- The octave test measures CURVATURE, not slope. At gain 0.5 / lacunarity 2
  every octave contributes equally to slope — that is what self-similar
  means — so a slope-based test reports no difference while the terrain
  visibly gains detail. My first version of that test was wrong, not the code.

Statics get one conform pass after composition; movers already clamp to the
terrain every tick in `step.rs`, so the player, villagers and car find the
ground themselves.

**Cars now touch the road.** Kenney authors vehicles origin-at-the-contact-
patch — tyres exactly on y=0, with each wheel node lifted by its own radius —
and every vehicle in every kit measures min.y == 0, verified across all 4442
GLBs in the library. We were dropping the model by `half.y` instead, a rule
that is right for a walker (a Mover's box bottom really is its feet) and
wrong for a raycast vehicle, whose suspension probes from the chassis origin.
The float was suspension travel plus wheel radius, 0.341 units, predicted in
closed form and matched by simulation to 1e-5. The same line also shifted in
world Y after rotation, so the mesh slid out from under a leaning chassis.
Both now derive from measured bounds along the body's own down axis — no
constant anywhere. Note the convention is real but NOT universal: track and
road pieces go to min.y = -1.0, so it must be read, never assumed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 16:59:45 +02:00
Admin
bb156b2ee6 Arcade: draw the HUD, pull the walking camera back, build headless again
**The HUD was never drawn.** `libs/game/render::hud` has existed and
gamemaker has called it all along; arcade simply never did. So the
interact prompt was computed every tick, published to `world.hud_slots`,
and thrown away — the get-in-a-car mechanic was discoverable only by
guessing the key. Same shape as the gamepad never being polled: the
capability was there and the app never called it.

Two things found while wiring it:

- The prompt asked for `size: 1.0`, which reads like a scale but is an
  absolute point size to the renderer — any value above zero is taken
  literally. It would have drawn a one-point speck. Now 17.0, deliberately
  above the 12.0 default, since this is the one line a player has to
  notice without being told to look.
- Added a standing controls hint. Nobody sits a child down with a manual,
  and the affordance prompt only appears once you are already next to
  something; this is what tells you how to get there.

**Arcade could not build headless.** The gamepad poll was added without
the `cfg(headless)` split gamemaker uses, and arcade had no `build.rs` to
define the cfg at all, so `MAKEPAD=headless` failed to compile and the
render-to-PNG path went with it.

**The walking camera sat too close** — at 6.5m the character filled a
fifth of the frame and hid the world behind them. Now 9.0m.

That broke a test asserting `car.distance > on_foot * 1.5`, and the fix
is the test, not the number. That ratio read like a decision but was
really the quotient of two values that happened to be current, so
correcting the walking boom on its own merits broke an assertion about
driving while nothing about driving had changed. Restated as a margin:
what has to hold is that getting into a car visibly widens the view, and
several metres does that at any walking distance.

Also logs gamepad connect/disconnect on change, so "the pad does nothing"
is answerable from a release log — either the app never saw the device or
it saw it and the mapping is at fault.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 20:30:42 +02:00