Files
rose-ash/sx/sx/native-browser.sx
giles d40a9c6796 sx-tools: WASM kernel updates, TW/CSSX rework, content refresh, new debugging tools
Build tooling: updated OCaml bootstrapper, compile-modules, bundle.sh, sx-build-all.
WASM browser: rebuilt sx_browser.bc.js/wasm, sx-platform-2.js, .sxbc bytecode files.
CSSX/Tailwind: reworked cssx.sx templates and tw-layout, added tw-type support.
Content: refreshed essays, plans, geography, reactive islands, docs, demos, handlers.
New tools: bisect_sxbc.sh, test-spa.js, render-trace.sx, morph playwright spec.
Tests: added test-match.sx, test-examples.sx, updated test-tw.sx and web tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 11:31:57 +00:00

157 lines
9.0 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
(defcomp
~applications/native-browser/content
()
(~docs/page
:title "Native SX Browser"
(~docs/section
:title "The idea"
:id "idea"
(p
"SX runs in web browsers today via a WASM kernel. This works, but it's indirect — the evaluator produces HTML, the browser's layout engine positions it, the browser's paint engine draws it. Three systems, two serialization boundaries, thirty million lines of code in between.")
(p
"A native SX browser eliminates the middleman. S-expressions go in, pixels come out. No HTML parser. No CSS cascade. No JavaScript engine. No DOM. Just the SX evaluator talking to a graphics library.")
(p
"The entire rendering pipeline — parse, evaluate, lay out, paint — runs in the same process, in the same language. The browser "
(em "is")
" an SX program."))
(~docs/section
:title "Why this is feasible"
:id "feasible"
(p
"The SX kernel already exists in OCaml: parser, CEK evaluator, bytecode VM, reactive signals, component model. It's roughly 3,500 lines. A native renderer adds about 1,200 lines on top — types, layout, painting, events, and the main loop.")
(p
"The hard parts of a web browser are HTML parsing (~500K lines in Blink), CSS layout (~800K lines), JavaScript execution (~2M lines in V8), and 2,000+ Web APIs. SX needs none of them. Components declare their structure as s-expressions. Layout is a pure function of the component tree. Painting is a walk over positioned boxes.")
(div
(~tw :tokens "overflow-x-auto rounded border border-stone-200 my-4")
(table
(~tw :tokens "w-full text-left text-sm")
(thead
(tr
(~tw :tokens "border-b border-stone-200 bg-stone-100")
(th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "Layer")
(th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "Web browser")
(th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "SX native")))
(tbody
(tr
(~tw :tokens "border-b border-stone-100")
(td (~tw :tokens "px-3 py-2 text-stone-700") "Parse")
(td
(~tw :tokens "px-3 py-2 text-stone-600")
"HTML parser (~500K lines)")
(td (~tw :tokens "px-3 py-2 text-stone-600") "SX parser (225 lines)"))
(tr
(~tw :tokens "border-b border-stone-100")
(td (~tw :tokens "px-3 py-2 text-stone-700") "Evaluate")
(td
(~tw :tokens "px-3 py-2 text-stone-600")
"V8 JavaScript engine (~2M lines)")
(td
(~tw :tokens "px-3 py-2 text-stone-600")
"CEK evaluator + bytecode VM (~1,300 lines)"))
(tr
(~tw :tokens "border-b border-stone-100")
(td (~tw :tokens "px-3 py-2 text-stone-700") "Layout")
(td
(~tw :tokens "px-3 py-2 text-stone-600")
"CSS cascade + flexbox + grid (~800K lines)")
(td
(~tw :tokens "px-3 py-2 text-stone-600")
"Flexbox subset (~250 lines)"))
(tr
(~tw :tokens "border-b border-stone-100")
(td (~tw :tokens "px-3 py-2 text-stone-700") "Paint")
(td
(~tw :tokens "px-3 py-2 text-stone-600")
"Composited layer tree + GPU raster")
(td
(~tw :tokens "px-3 py-2 text-stone-600")
"Cairo 2D draw calls (~150 lines)"))
(tr
(td (~tw :tokens "px-3 py-2 text-stone-700 font-semibold") "Total")
(td
(~tw :tokens "px-3 py-2 text-stone-700 font-semibold")
"~35 million lines")
(td
(~tw :tokens "px-3 py-2 text-stone-700 font-semibold")
"~5,000 lines"))))))
(~docs/section
:title "Architecture"
:id "architecture"
(p
"The native host sits alongside the existing browser and server hosts. It reuses the kernel unchanged and adds a rendering pipeline that converts SX value trees to positioned boxes to pixels.")
(~docs/code
:src (highlight
"SX source\n | sx_parser\n v\nSX value tree\n | CEK/VM eval (component expansion)\n v\nExpanded tree\n | render tree builder\n v\nRender nodes (tag, style, children, event handlers)\n | layout engine (flexbox)\n v\nPositioned boxes (x, y, width, height)\n | painter (Cairo + Pango)\n v\nPixels in window (SDL2)"
"text"))
(p
"Each layer is a pure function of the previous. Change a signal, re-evaluate the subtree, re-layout, re-paint. The reactive system from the web version works identically — "
(code "swap!")
" triggers "
(code "notify-subscribers")
" triggers repaint."))
(~docs/section
:title "The platform interface"
:id "platform"
(p
"A web browser provides thousands of APIs. The native SX browser needs about fifteen primitives — the minimal surface that SX code calls into for rendering and input:")
(~docs/code
:src (highlight
";; Window\n(window-create width height title)\n(poll-event) ; → {:type \"click\" :x 100 :y 200} or nil\n(blit) ; flush surface to screen\n\n;; Drawing\n(draw-rect x y w h color)\n(draw-rounded-rect x y w h radius color)\n(draw-text x y text font-size weight color)\n(measure-text text font-size weight) ; → {:width N :height N}\n(fill-background color)\n(set-clip x y w h)\n(clear-clip)"
"lisp"))
(p
"Everything above these primitives — layout algorithm, style resolution, hit testing, reactive scheduling, component expansion — is SX evaluating SX. The browser is a program written in the language it renders."))
(~docs/section
:title "The strange loop"
:id "strange-loop"
(p
"The native browser can be written "
(em "in SX itself")
". The layout engine is SX functions. The style parser is SX pattern matching. The event dispatcher is SX closures. The render tree is SX data. Only the fifteen leaf primitives are foreign.")
(p "This means the browser is:")
(ul
(~tw :tokens "list-disc list-inside space-y-2 mt-2")
(li
(strong "CID-addressable")
" — the layout engine has a content hash, the style system has a content hash, the event model has a content hash")
(li
(strong "Hot-swappable")
" — replace the layout algorithm at runtime by loading a different CID")
(li
(strong "Self-hosting")
" — the browser renders itself, if you point it at its own source")
(li
(strong "Verifiable")
" — the provenance chain from spec to bytecode to running browser is all content-addressed s-expressions")))
(~docs/section
:title "Adoption path"
:id "adoption"
(p
"The native browser doesn't need to replace the web. It sits alongside it, the same way React Native apps, Electron apps, and Flutter apps coexist with web browsers.")
(ul
(~tw :tokens "list-disc list-inside space-y-2 mt-2")
(li
(strong "Today:")
" SX runs in standard web browsers via the WASM kernel. No install, no app store.")
(li
(strong "Traction:")
" Sites built with SX prove the component model, reactive signals, and bytecode compilation on real traffic.")
(li
(strong "Native:")
" The same SX components render natively. Same code, no HTML middleman. Faster, lighter, offline-capable.")
(li
(strong "Platforms:")
" SDL2 gives Linux, macOS, and Windows. A future host with platform-native rendering gives iOS and Android."))
(p
"The difference from Electron: Electron ships an entire Chromium (~200MB). The native SX browser is a 5-10MB binary — the OCaml kernel plus Cairo and SDL2."))
(~docs/section
:title "Proof of concept"
:id "poc"
(p
"The POC renders a single page: a reactive counter with buttons, text, and flexbox layout. It demonstrates the full pipeline from SX source to interactive pixels.")
(~docs/code
:src (highlight
"(let ((count (signal 0)))\n (div :class \"flex flex-col items-center gap-6 p-8 bg-stone-50\"\n (h1 :class \"text-3xl font-bold text-stone-800\"\n \"SX Native Counter\")\n (div :class \"flex items-center gap-4\"\n (button :class \"bg-violet-600 text-white px-4 py-2 rounded-lg\"\n :on-click (fn () (swap! count dec)) \"\")\n (span :class \"text-4xl font-bold\" (deref count))\n (button :class \"bg-violet-600 text-white px-4 py-2 rounded-lg\"\n :on-click (fn () (swap! count inc)) \"+\"))\n (p :class \"text-stone-500 text-sm\"\n \"Native rendering — no HTML, no CSS, no JavaScript\")))"
"lisp"))
(p
"Click the button. The signal updates. The display changes. No browser involved."))))