Repository navigation
Hide the xterm viewport scrollbar in the mandelbrot and pathtrace shells - #54
Merged
Merged
Conversation
xterm.css sets .xterm-viewport to overflow-y:scroll, which reserves a scrollbar even with scrollback:0. Wherever scrollbars are classic (Linux, Windows, macOS with a mouse attached) that painted a 15px bar over the right edge of the mandelbrot and pathtrace images. Nothing scrolls in either demo, so hide it; it needs !important because the inlined xterm.css comes after the shell's styles. Headless Chromium hides scrollbars by default, so CI never saw it. The viewport test now launches with scrollbars drawn and fails on any gutter; against the current live site it reports 15px at both sizes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
mparrett
added a commit
that referenced
this pull request
Oct 11, 2026
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>
mparrett
added a commit
that referenced
this pull request
Oct 11, 2026
…ages, a sixel page alongside (#55) * mandelbrot: encode sixel bands from the coarse grid, 3-5x faster 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> * pathtrace: scalar tracer, 2.5x faster, same image 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> * pathtrace: draw to a <canvas> in the browser; terminals keep PNG frames 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> * mandelbrot: draw to a <canvas> in the browser; terminals keep sixel 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> * demos: read the clock with System/currentTimeMillis, so TinyGo builds 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> * mandelbrot-sixel: keep a sixel page beside the canvas one 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> * pages: build the site with TinyGo 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> --------- 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.
Fixes the stray scrollbar beside the mandelbrot and pathtrace terminals.
xterm.css sets
.xterm-viewporttooverflow-y: scroll, which reserves a scrollbar even withscrollback: 0. Wherever scrollbars are classic (Linux, Windows, macOS with a mouse attached), that paints a 15px bar over the right edge of the image. Nothing scrolls in either demo, so both shells now set.xterm .xterm-viewport { overflow-y: hidden !important; }. The!importantis needed because the inlined xterm.css comes after the shell's own styles; without it the rule has no effect.CI never caught this because headless Chromium hides scrollbars by default.
viewport_fit_test.pynow launches without--hide-scrollbarsand fails if the xterm viewport has a scrollbar gutter.Validation:
🤖 Generated with Claude Code