The pedigree tree booted one WebGL context per node card (pooled by cullCanvasNodes
to on-screen nodes). A tree with many nodes visible at once could still exceed the
browser's ~16-context cap and blank/flicker. Replace it with installTreeScissor: one
renderer per .buddy-canvas (sitting above its dot-grid, below the panning card layer)
draws every node into its on-screen rect via the scissor test, clipped to the canvas
viewport — so the tree uses exactly 1 context regardless of size.
Node cards become transparent windows onto the shared canvas: the A/B badge moves
into the footer and selection shows as a card ring (both outside the thumb) so the
behind-content canvas never occludes them. The renderer keys on its real canvas
child (not a JS-prop lock) so it re-attaches after Gradio swaps the tree HTML, and
the loop self-disposes when its canvas is stripped. cullCanvasNodes is now a no-op.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
fix(tools): place shared GL canvas behind content so page text shows
The single scissor canvas was z-index:50 (on top, pointer-events:none). That
composites fine in DevTools screenshots but on real Chrome the full-viewport
transparent WebGL layer occluded all page text (header, filter pills, card names).
Move it to z-index:-1 behind content (three.js multi-element pattern): the page
backdrop moves to <html>, <body> and card stages go transparent so the 3D shows
through the stage windows while all text/chrome paints normally on top.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
perf(tools): render all report cards from one WebGL context via scissor
Replace the per-card-context approach (capped at ~12 to stay under the browser's
~16-context hard limit, which also caused flicker as the browser killed and
recreated the oldest contexts) with a single shared renderer that draws every
card into its on-screen rect via the scissor test. Removes the cap entirely: all
106 parts / 52 creatures stay live at once with no flicker. Per-frame transparent
clear avoids scroll ghosting; per-card 256px shadow maps bound memory; minimal
document-level pointer hit-testing keeps drag-to-orbit.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(tools): live-3D Part Catalog page + compendium rework
Both report pages render every part/creature with the real in-game rig (shared
voxel-rig.js) instead of static thumbnails: production lighting, contact shadows,
ACES tone mapping, turntable, drag-to-orbit. A shared IntersectionObserver
context-cap manager (voxel-cards.js) keeps live WebGL contexts capped (~12,
nearest-to-viewport-center prioritized) so 106 parts / 52 creatures never blow the
browser's context limit. Part Catalog adds category sections + a full table.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(tools): extract shared voxel rig + parts-catalog data sync
Translate cosmetic Chinese (comments, report title, spec mockup) to English.
Keep the load-bearing CJK synonym tables in fallback.py that let the offline
interpreter recognise Chinese command input.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(compendium): expand to 52 broad creatures + shared-renderer report
- Remove IP-adjacent content; 52 original creatures across mammal/mythical/
aquatic/plant/bird/alien/bug/reptile/mascot/robot/amphibian themes via
parametric builders (_quad/_biped/_bird/_blob/_bug/_fish/_serpent) + 16
hand-authored specials.
- report.html: one shared offscreen WebGLRenderer snapshots each creature to an
<img> instead of 52 live contexts (the ~16 WebGL-context cap blanked most
cards). Static 0.7 rad yaw.
- Re-sync report_data.js (52 creatures).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(compendium): data module + report tool + first 12 creatures
Claude-authored freeform creatures across themes (alien/mythical/plant/aquatic/
mascot/bug). compendium.py exposes all_entries/get/random_sample/as_genome;
compendium_data.py holds the growing roster (toward ~50). tools/compendium/
report.html (+ sync_report_data.py) renders the whole roster with the app's
AO voxelizer so the set can be browsed offline anytime.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
chore(tools): no-store meta on report so local reloads aren't stale
Python's http.server sends no cache headers, so the browser could serve an old
report.html after edits (looked like an unfixed bleed-through). Add a no-store
meta so a normal reload always fetches the latest.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
fix(tools): opaque controls bar so scrolled cells don't bleed through
The sticky header used a translucent gradient (bottom alpha 0.93) plus
backdrop-filter:blur, so matrix cells scrolling behind it showed through the
bar. Make the header fully opaque (solid var(--bg), no blur).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Pin the matrix theme-header row (and corner) below the controls bar on
scroll. It was inside an overflow:auto container that doesn't itself scroll,
which silently disabled vertical stickiness; drop the overflow so the body is
the scroll container, and offset the row by the controls-bar height (--header-h,
measured in JS).
- Remove the leftover Chinese theme labels (PROMPT_ZH) and lang="zh-Hant"; the
report is version-controlled and must be English-only. Theme ids (cat, snake,
...) are already English, so they are shown directly.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
8-theme grammar-enforced run, merged into report_data.js (now 20 models).
Geometry is rough-blocky, consistent with the spike's verdict that quality
does not track model size/family outside the Gemma 4 line.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Re-running a model that is already in report_data.js previously appended a
second set of rows (duplicate model in the matrix). Add merge_rows(): drop
existing rows whose model label is being re-tested, then append the fresh
run. New models still append; brand-new themes need the model re-run.
Document the behavior in README and cover merge_rows with unit tests.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(tools): add View menu + model×theme matrix to the eval report
The report was a single flat card list. Add a View dropdown that switches
between:
- Gallery: the existing big rotatable cards + theme filter (detail view).
- Matrix: a model (rows) × theme (columns) comparison table.
Matrix cells are static snapshots from one shared offscreen WebGLRenderer
(the app's thumbnail strategy), rendered lazily on scroll-in — flat context
count, so all 152 cells display without tripping the WebGL context cap.
Sticky theme header row and model column. The theme filter is hidden in
matrix mode (themes are the columns there).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Consolidate the throwaway scratch/voxel-spike/ exploration (11 scripts,
scattered HTML viewers) into a tracked, reusable tool so the next model
test does not start from zero.
tools/voxel-model-eval/:
- core.py: pure base (SYS prompt, 8 themes, BOX_SCHEMA, extract_json,
voxelize, metrics); no model loading, llama_cpp never imported here.
- models.py: the editable (label, repo) list.
- exemplars.py: few-shot reference layouts for --fewshot.
- eval.py: single CLI; one model at a time (download -> grammar-enforced
test -> optional --delete-after -> merge); llama_cpp imported lazily so
the pytest suite runs without the sibling venv.
- report.html: Three.js viewer with a theme filter; live WebGL contexts
are reconciled by distance-to-viewport-center on scroll (fixes the
evict-furthest blanking bug where still-visible cards never re-mounted).
- report_data.js: committed 19-model x 8-theme sweep so the report opens
populated.
Align voxelize() to JS Math.round (round half up) so P…