Repository navigation
demos: canvas in the browser, faster sixel and pathtrace, TinyGo on Pages, a sixel page alongside - #55
Merged
Conversation
Each coarse pixel is a scale x scale block, so the encoder read every grid value scale^2 times. It now ORs a band's bits per grid cell (a 6px band at scale 3 covers 2 grid rows) into a reused int-array of per-colour mask rows CW wide, and RLE-encodes each row with run lengths multiplied by the scale: runs are maximal in both, so the encoding is the same. Colours come out in register order instead of hash-map order. Checked on four views (home at scales 3 and 2, a zoom-180 seahorse at maxiter 320, a scale-1 zoom-40): every band decodes to the same colour rows as before and the frames are the same size; encode time 521->152, 244->97, 792->165, 90->67 ms on the VM. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The tracer built small [x y z] vectors for every vector op of every sample (nth and vector were ~13% of the profile). It now works on doubles: each scene is a flat double-array, 10 per sphere, the ray state lives in one reused double-array, and radiance walks the path forward recording each bounce's albedo, then folds back from the end, so e + albedo*L is computed in the same order as the recursion. The arithmetic follows the vec3 helpers' order and rand is drawn in the same order, so out.png is byte-identical. The camera basis is computed once per pass instead of per pixel (same values). Bench (32 passes, scene 0): 63.5 s -> 25.7 s; scene 1 at 4 passes: 8.2 s -> 3.3 s. out.png identical for both scenes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In the browser (surface/available?) each pass goes to the page as an RGBA framebuffer through surface/present and the status through js/emit, the way ptcanvas works. No PNG encode, no base64, no escape for xterm to parse, and the image scales to the page (up to 720px wide) instead of a fixed 320x240. The shell is now a canvas page with a status line, the same keys, and buttons for touch (orbit, height, scene, reset). The canvas tone curve reads a 4096-entry gamma table instead of math/pow per channel (within one 8-bit step of the exact curve); the PNG path for terminals keeps the exact curve, so out.png is unchanged. Browser, stock Go, per pass: compute 6.1-6.3 s (15.3 s before the scalar tracer), present 0.54-0.63 s (PNG encode was 2.2 s). Native, in tmux: ~0.8 s a pass; scene switching and the headless bench unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In the browser (surface/available?) each frame goes to the page as an RGBA framebuffer, one pixel per grid point, through surface/present, with the status through js/emit. No sixel is built or parsed, so the emit phase is gone: a home-view frame on stock-Go wasm is ~3.7 s compute + ~0.3 s present instead of ~2.9 s + ~3.9 s sixel emit. The page sizes the canvas to the window (973x730 at 1280x900, 358x268 on a 390px phone, against a fixed 480x360 sixel), dims it while computing, and adds buttons for touch. Taps go back as the SGR mouse strings the program already parses, on a grid click-res (4) times finer than the frame; the new canvas-click->center maps them straight to the complex plane, so the scale handshake and terminal geometry are no longer needed in the browser. Native terminals keep sixel and their click mapping unchanged. test/viewport_fit_test.py is rewritten for the canvas: no horizontal overflow and the whole canvas visible at 390px and desktop, a centre tap zooms in and keeps x within one grid cell, an off-centre tap pans without zooming, shift-click zooms out, and the wheel, "+" and the Zoom out button work. The status report carries vw so zoom steps are measurable. Passes against a build with v1.13.0 (the CI pin). The README's browser screenshot is the canvas page. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… run
The timing readouts used (inst-ms (now)). Under TinyGo that fails on the
first frame ("method UnixMilli on go.time.Time is unavailable under
TinyGo") on any let-go without nooga/let-go#1054, including v1.13.0, the
Pages and CI pin. A small now-ms helper reads System/currentTimeMillis,
the same clock, in mandelbrot, pathtrace and ptcanvas. Headless benches
unchanged on v1.13.0.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
demos/mandelbrot-sixel is the same program (a symlink to mandelbrot.lg) with the xterm shell, including #54's hidden viewport scrollbar. That shell never attaches a canvas sink, so surface/available? stays false and the program draws sixel in xterm.js, scale handshake, dim cue and terminal-cell clicks included. The previous viewport test, with #54's scrollbar check, comes back as test/viewport_fit_sixel_test.py, with its own ci.sh block (ci.sh now also waits for each server to exit before the next group reuses the port). On Pages it is one more entry in build-pages.sh's DEMOS, at /mandelbrot-sixel/, with a tile on the index (a screenshot of the page at its home view) and an entry in test/pages_site_test.py. The index's mandelbrot and pathtrace captions now say canvas. CLAUDE.md notes that the canvas pages use surface/present and how the sixel page ends up on the sixel path. pathtrace drops the OSC 5002 geometry announcement, which only its old xterm page read. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
TinyGo builds run about twice as fast in the browser as stock Go and are a third of the download (docs/profiling-2026-10.md). The workflow installs TinyGo 0.42.0 (let-go CI's pin) and sets LETGO_USE_TINYGO=1 and LETGO_TINYGO_OPT=2, which lg -w and microgpt's wasm/build.sh read. A TinyGo page takes 4-5 minutes to build and there are now five (pull requests build them too), so the timeout goes to 60. Checked by building _site locally as the workflow does (v1.13.0, TinyGo 0.42.0, -opt=2): pages are 2.8 MB (8.8 MB stock), and a mandelbrot frame is 1.3 s compute + 0.13 s present. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Rebased onto main after #47, #51, #53, #54 and #56. Optimizations 1–4 from the October profiling (#50), plus a sixel page kept beside the canvas one.
What changes
[x y z]vectors per opout.pngbyte-identical for both scenessurface/presentin the browser, like ptcanvas; status viajs/emitpages.ymlinstalls TinyGo 0.42.0 and builds withLETGO_USE_TINYGO=1 LETGO_TINYGO_OPT=2; five pages, timeout 60 mindemos/mandelbrot-sixel= the samemandelbrot.lg(symlink) with the xterm shell (incl. #54's scrollbar fix), at/mandelbrot-sixel/on Pages with its own index tilesurface/available?stays false)Terminals are unchanged: mandelbrot still draws sixel (now with the faster encoder), pathtrace still draws PNG through iTerm2 inline images.
Smaller pieces: the demos read the clock with
System/currentTimeMillis, because(inst-ms (now))fails under TinyGo on let-go without nooga/let-go#1054 (v1.13.0 included); the canvas pages add buttons for touch; the README's browser screenshot is the canvas page.Compatibility
The canvas pages need
surface/present(#572), in let-go releases since v1.13.0, which is whatconfig/let-go-releasepins; on an older lg they load and draw nothing. CLAUDE.md notes how the sixel page ends up on the sixel path.Tests
test/viewport_fit_test.pyis rewritten for the canvas page: no horizontal overflow and the whole canvas visible at 390px and desktop, a centre tap zooms in and keeps x within a grid cell, an off-centre tap pans without zooming, shift-click zooms out, and the wheel,+and the Zoom out button work.test/viewport_fit_sixel_test.pyagainst the sixel page (fit, click mapping, resize race), with its ownci.shblock.scripts/ci.shagainst v1.13.0 with playwright passes every group (shellcheck isn't installed here)._siteexactly aspages.ymldoes it (24 min, five pages at 2.7–2.8 MB) passestest/pages_site_test.py: index tiles, images, links, the old start-view redirect, and every demo, the sixel page included, isolated by its own service worker and booting without errors.🤖 Generated with Claude Code