Three panels of #![no_std] Rust compiled to plain JavaScript by a rustc codegen backend: no WASM, no bundler. The browser loads /demo/app.js as an ordinary script and calls exported functions by name.
The same compiler also drives the DOM: the counter island is server rendered markup that compiled Rust takes over in the browser, and the nesting island does it through two component boundaries, and the searching island asks the server for its contents.
What that compiler is for is on the showcase: a board that plays itself, sand that falls under the pointer, and a minefield that floods open from a click. Three programs, none of which the expression language that came before could have said at all.
This toggle is Topcoat's own expression runtime: a signal, a few hundred bytes of generated script. The panels below are the other approach, a whole Rust crate compiled ahead of time. Both coexist on one page and neither knows about the other.
function life_step: slot indexing, cur.buf[cur.off + index], and the | 0 and >>> 0 masks that keep Rust's integer widths. It really is just JavaScript.living_neighbours: the neighbour offsets as a slice literal, { buf: [[-1, -1], ...], off: 0, len: 8 }, walked by core's own slice iterator.panic_demo, spelled { filename: "demo-app/client/fail.rs", line: ..., col: ... }. They are arguments, filled in at the call site: that is what #[track_caller] compiles to.app$fmt$impl_0_write_str, commented as <fmt::Emit as core::fmt::Write>::write_str, calling __rt.js_emit(piece). That one call is the entire foreign function interface of the formatting panel.fmt_demo: the honest counterweight, because core::fmt is deep and all of it had to be compiled.The exported names are real globals. Open the console and try life_population({buf: [true, true, false], off: 0, len: 3}).
The boards are JavaScript arrays. Rust receives them as &[bool] and &mut [bool], which the backend represents as { buf, off, len } over the array itself. Nothing is copied and nothing is serialized: the compiled code indexes the same array the page holds.
generation 0, population 0. Click a cell to toggle it.
Every keystroke calls compiled core::fmt. Width and precision are runtime values, but fill and alignment are compile time: core::fmt bakes them into the format string, so the Rust side is a matrix of write! arms. Floats are missing on purpose: Display for f64 is not implemented by the backend yet.
The location below is a Rust file, line, and column, read from PanicInfo inside the compiled crate's #[panic_handler]. The two unwrap choices report different lines because of a single #[track_caller] attribute.
demo-app/client/fail.rs. The source map embeds the Rust text, so it is really there.unwrap() line in Rust, and the call stack names panic_demo and unwrap_here. The compiler's own glue is hidden, because the map lists it in its ignoreList.The page keeps working after a panic. State is owned by JavaScript and the exported functions are pure, so a panic is an exception, not corruption.