Fix first load flash and black screen: no pow() of a negative base in the intro sweep shader (NaN on real GPUs, spread by bloom), guarded normal and rim terms, service worker serves exact ?v= matches only (older copies are the offline fallback), cache bust nan1
Chase
b24a488
Starting view fitted to each roll: distance from the roll's height and width, roll centred in the screen area the control panel, header, readout and scrubber leave free, refit on resize until the camera is moved; changelog
Chase
bf9ab42
Lighter mesh for phones: tools/build.py --coarsen N (and UNROLL_STEP) keeps every Nth sampled row and column of a built mesh with the same UV mapping; PHerc0172 lite mesh 527,834 vertices (from 2,082,140), 1.5 MB packed, loaded automatically on phones via geometry.lite (?mesh=full or ?mesh=lite overrides); tear threshold follows the mesh step
Chase
0d6db02
CT volume view (lib/ctvolume.js, lib/ctsource.js): a cube of the raw X-ray scan around a picked papyrus point (256 voxels, 128 on phones) as a Data3DTexture, raymarched MIP clipped to an oriented slab that starts square to the surface; own canvas beside the scroll, render on demand; panel group (Pick a point, Show volume) and floating panel with slab, tilt, window/level, size and detail controls; Settings, unrollView.ct.volume API and actions, ?ctvol URLs, scanGeom and scanPick hooks in app.js, glossary (MIP, slab, CT volume, raymarching), docs
Chase Hendrick
42b53b4
X-ray window/level with percentile auto contrast (lib/ctwl.js): uCTWin uniform in the surface shader, X-ray contrast panel group (Window, Level, Auto contrast, Full range), Settings (auto on load, low and high percentiles), unrollView.ct API and actions, ?ctwin ?ctlevel ?ctauto URLs, glossary entries, docs
Chase Hendrick
ce6b88b
Lower resolution while dragging (lib/dragres.js): renderer and composer pixel ratio drop on OrbitControls start and return 150 ms after the camera settles; Settings and panel control (off, light, strong), unrollView.dragRes API, ?dragRes= URL, glossary entry, docs/CT_VIEW.md; fix undefined prevClear in hover pick
Chase Hendrick
1c31760
Public API at window.unroll (schema, help, coverage test; ui and bench groups), embed mode with postMessage bridge and oEmbed, version 0.9.4 in About and API, ink_9um (9 µm) label and credit, Ink Bench hardening from the audit, hashchange without reload, capped test hooks
Overlay textures upload through a per-frame queue (about 6 ms per frame, initTexture) instead of all at once, removing the 14.6 s main-thread freeze; import map versions for tear.js, damage.js and lib/*.js; scan shown as 9.4 µm with 9.362 µm voxels in the details; glossary links every occurrence; mobile panel clears the bottom bar
Control panel redesign (ui.js, ui.css): grouped Layers, Ink Bench, View, Destroy and Scrolls with icon and text labels, 44 px targets, pressed states, shortcut tooltips, focus rings, docked mobile panel; Settings (quality, reduced motion, speed, glow, background, HUD, labels, sensitivity, units, Ink Bench button) saved in localStorage and settable by URL; agent API unrollView.actions/settings/state/glossary (docs/AGENT_API_UI.md); plain language glossary.json under About with linked terms; faster intro (motion within one frame, ease-out, about 2.5 s, skip with Enter or button, follows speed) with shaders compiled behind the loader; CPU pick fallback for Destroy on GPUs without float targets; readout panel, fixed Roll up width, header clear of the panel
Sandbox debris: triangles bound to removed nodes are hidden instead of hanging frozen; tiny loose pieces become crumbs, small pieces held by a glued node are freed, a sheet only sleeps when no free piece is off the floor; crumbs and flakes settle and fade like the main view; cache-busting ?v= on the JS modules; sandboxStats/sandboxSim test hooks
Multi-scroll support: build.py and fetch.py take any open-data scroll (per-scroll voxel size, X-ray scan, renumbering by radius, optional ink, a second 1 um ink layer), make_manifest.py, the scroll picker's prize eligible badge and eligible-only toggle (window.unrollScrolls), and a 'Why N ink layers?' box written from each manifest's ink layers
Destroy: floor follows the scroll as drawn (rolled or unrolled) with a soft visible floor glow; smoke, ash, sparks and burnt flakes rise from each ember's current spot on the deformed sheet; new Crumble (K, unrollView.crumble()) breaks the scroll up from its edges into a heap of dust, Reset restores it; help and README
Faster first load: ship the int16 geometry as byte planes + gzip (scroll.i16p.gz, 1.62 MB instead of 3.48 MB), unpacked in the browser to the exact scroll.bin bytes with a fallback to scroll.bin; tools/pack_bin.py builds it, tests/pack_bin.mjs checks the round trip
Destroy debris: landed shards and dust fade out soon after landing instead of resting 25 to 30 s on the undrawn floor, where they read as hanging in mid-air; reset clears every instanced slot; watchdog retires anything drawn and unmoving for 2.5 s; damageStats reports stillMax, stillAirMax, retired
Ink Bench: score your own ink prediction against the published ink, in the browser, with a Python and CLI companion, an open bundle format, a leaderboard view and Apache-2.0 code
Destroy: fibrous alpha-tested damage edges, Burn and Water tools with cursors, Roll up, size-dependent debris that settles on the floor, wall-clock burn timing; rename Tear apart to Destroy; ink model text reads its coverage from the manifest
- scrolls/index.json registry and per-scroll manifests (windings with ranges, ink layers with sources and resolutions, credits, data terms); PHerc. 139 migrated with no visible change
- scroll picker (hidden with one scroll), per-scroll and per-winding lazy loading
- lib/manifest.js validator shared by the page and tools/validate_scroll.mjs; docs/ADDING_A_SCROLL.md; tools/precompute_template.py
- Open your own: files, folders, zips or a URL, read in the browser only (nothing uploaded), OBJ/PLY with UVs, PNG/JPG/TIFF layers, size limits, clear errors, kept in IndexedDB and labelled local; downloadable synthetic example package
- atlases as WebP with JPG fallback (about 2.1 MB smaller)
- rename layers: Published ink (2.4 µm) and Our ink model (9 µm)
Polish: keyboard shortcuts with a ? help overlay, V to reset the camera, lazy-load the ZeroGPU textures, preload data and three.js in parallel, fix phone layout overlap and desktop title overlap, vignette and papyrus sheen, full OG and Twitter tags with a new rendered preview