Seven frameworks researched against primary sources, plus freya, vizia and floem added 2026-08-03 with a narrower upstream check. Ten comparable specifications built across the cohort — eight in each of the ten frameworks (80 apps), plus iteration 5's two specs in all ten and in Dioxus Native as an eleventh entry (102 apps) — and the dependency trees compared by experiment round. Revised 2026-08-30 after maintainer feedback on r/rust: every upstream-facing claim was re-verified against the pinned crate sources and current upstream; corrections are marked inline with the date, listed in report/data/corrections-2026-08-30.md, and the resulting issue list is report/data/upstream-issues.md. Two questions. Where exactly is the fragmentation?
And how do you actually ship a cross-platform desktop app in Rust today?
The ecosystem is consolidating from the bottom of the stack upward.
In the last 12 months the principal reusable pure-Rust text stacks converged on HarfRust, Zed
dropped its custom Linux GPU layer for wgpu, and Slint/Bevy/floem migrated to parley/fontique. AccessKit is now the
shared native accessibility layer. iced and floem remain unintegrated (floem's pinned rev has no
accesskit anywhere in its dependency tree), while gpui support is unreleased and
currently disabled in Zed.
The top is still fragmented: text layout, 2D renderers, widgets, and windowing forks.
raw-window-handle is the only shared cross-platform window-handle crate. Some of those 28 are platform-specific GUI crates. Recomputed 2026-08-03 over all ten *-app lockfiles; 16 of the 28 carry version skew.| Capability | iced | egui | gpui | tauri | xilem | slint | dioxus | freya | vizia | floem |
|---|---|---|---|---|---|---|---|---|---|---|
| Windowing | winit | winit | in-houseper-platform | taowinit fork | winit | winit+qt, linuxkms | taowinit migration proposed | winitvia freya-winit | winit+glutin GL context | floem-winitwinit fork, git-only |
| GPU / render backend | wgpu | wgpu | Metal/D3Dtested 0.2.2: blade on linux; zed main: wgpu '26, unreleased | webview | wgpu | backend-dependentGL/software in tree; wgpu/Skia opt-in | webview | Skia/Metalvia freya-skia fork | Skia/GLglutin context | wgpusoftbuffer fallback |
| 2D renderer | iced_wgpu | epaint | in-house | webview | vello | femtovg+ own software; Skia/Qt opt-in | webviewblitz 0.7.9: classic Vello | freya-skia 0.98rust-skia fork | skia-safere-exported as vizia::vg | floem-vgervger-rs fork; tiny-skia sw |
| Text shaping | harfrustvia cosmic-text | harfrustin epaint 0.34+ | platformCoreText/DWrite; linux (0.2.2): cosmic-text/rustybuzz | webview | harfrustvia parley | harfrustvia parley | webview | HarfBuzzC++, inside Skia | HarfBuzzC++, inside Skia | harfrustvia parley |
| Text layout | cosmic-text | galleyshapes RTL runs; no paragraph BiDi | in-house | webview | parley | parley | webviewblitz: parley | Skia textlayoutICU BiDi | SkParagraphSkia textlayout | parleycosmic-text on stale 0.2.0 |
| Font fallback | fontdbdormant since '24 | bundled | platformlinux (0.2.2): fontdb | webview | fontique | fontique | webview | platformSkFontMgr/CoreText | platformSkFontMgr/CoreText | fontiqueHan/kana unresolved on macOS |
| Widget layout | in-house | in-house | taffy | CSS | in-house | DSL+ taffy (exp.) | CSSblitz: taffy+stylo | torinown engine | morphorm+ real CSS engine | taffymin-content trap |
| Widgets | in-house | in-house | low-level elementsno reusable 1st-party suite; gpui-component is 3rd-party | HTML/JS | in-house~15 views | in-house | HTML/JS | in-houseInput is single-line | in-houseincl. VirtualTable | in-house+ understory_* git crates |
| Accessibility | missingdraft PR #3111 | AccessKitschema required; eframe adapter default | unreleasedmerged 2026-05 | webview | AccessKit | AccessKitdefault on Winit desktop | webview | AccessKitinline per-element attrs | AccessKitdefault feature | missingno accesskit in tree |
| IME | winitsince 0.14 | winit | platformwindows rough; 2026-08-08 run: 7/8 apps alive+window, IME itself unexercised | webview | winit | winit | webview | winiton_ime_preedit event | winit | winit fork |
| Styling / theming | in-house | in-house | tailwind-like | CSS | in-house | DSL | CSS | in-house | CSS enginevizia_style; transitions | in-housereactive style fns |
hotpatching feature (via dioxus-devtools), an in-progress iced integration (iced#3000), and Axum servers; since 0.7.0 the dx CLI works on any Rust project (the iced author's cargo-hot is a self-described broken exploration). Verified upstream 2026-08-08; not exercised in this corpus. Sources in report/data/hotpatch-convergence.md.A GUI framework is a tower of layers: window (winit opens the window and feeds you input) → GPU (wgpu turns one API into Metal/Vulkan/DX12) → 2D renderer (turns "rounded rect with shadow" into GPU work or pixels) → text (find fonts → shape glyphs → lay out lines) → layout (taffy computes flexbox math) → a11y (AccessKit feeds screen readers) → widgets. Several lower-layer pieces are increasingly shared. These four stories are where sharing still breaks down.
Text is a pipeline: discover fonts → shape glyphs → lay out lines. The bottom stage just consolidated. HarfRust (the HarfBuzz org's official Rust port) now shapes for cosmic-text, parley and egui. The layout stage is the genuinely expensive part: line breaking, wrapping, bidirectional text, rich text, caret and editing semantics. It still exists four times:
| Stack | Owner | Used by | Known holes |
|---|---|---|---|
| cosmic-text | System76 (for COSMIC) | iced, COSMIC, gpui-on-Linux | fallback via dormant fontdb |
| parley | Linebender | xilem, Slint, Blitz, Bevy, floem | egui adoption stalled on API mismatch |
| epaint "galley" | egui, in-house | egui only | no paragraph-level BiDi reordering; no color emoji |
| line_layout | Zed, in-house | gpui only | quality differs per OS (platform shapers) |
The three frameworks added on 2026-08-03 do not add a fifth Rust stack: freya and vizia both delegate
layout to Skia's C++ textlayout/SkParagraph over CoreText, and floem's main swapped
cosmic-text for parley wholesale, after which its macOS fallback failed to resolve Han/kana at the pinned rev.
Why it persists. Both leading stacks have sustained institutional contributors: System76 employees on cosmic-text, and a mix of grants and employer-backed contributors around Linebender. Both stay active. egui tried adopting parley (egui#5784) and hit a real architecture mismatch: parley wants to own "a rectangle to fill with text"; immediate-mode egui re-derives layout every frame. Iced adopted cosmic-text in 2023; the retained primary sources establish the adoption, not why it was chosen over Parley.
Why you care. Four stacks means four independent sets of text bugs. egui shapes individual Arabic/Hebrew runs but cannot reorder multiword or mixed-direction paragraphs correctly (egui#1016); Slint's run reordering and tested selection work, while explicit base direction, UI mirroring, and codepoint-oriented backspace remain gaps. Momentum favors parley (3 frameworks migrated in 12 months) but cosmic-text won't vanish while COSMIC ships on it.
Reusable native stacks increasingly share wgpu, but renderer implementations still target a mix of wgpu, platform GPU APIs, OpenGL/Skia, and CPU software. In this sample the framework-side layer includes vello (Linebender, GPU compute), epaint (egui, CPU tessellation), iced_wgpu, femtovg (Slint default), Skia bindings (Slint premium, vizia via upstream skia-safe, freya via its own freya-skia fork), vger (floem, via a fork of vger-rs), and gpui's shader pipeline ("render like a videogame", one shader per primitive).
Why it persists. Genuinely different philosophies (compute-shader vector rendering vs tessellation vs SDF shaders), plus Slint's need to run on GPU-less microcontrollers. Vello is one prospective shared renderer, alongside reusable Skia, FemtoVG, and tiny-skia paths; classic Vello is alpha, and Linebender calls only the Hybrid path roughly beta.
Why you care. The six-plus renderer families duplicate some anti-aliasing, clipping, glyph-atlas, and GPU-driver work, but this audit did not quantify a multiplier. This layer consolidates last, if ever. The realistic near-term win is sharing the bricks (egui already adopted vello_cpu for glyph rasterization) rather than whole renderers.
In 2021 Tauri forked winit into tao because it needed native menus/tray and, decisively, GTK-hosted windows on Linux, since WebKitGTK (Wry/Tauri's supported Linux system-webview backend) can only render inside GTK containers. Menus and tray have since been extracted into winit-compatible crates (muda, tray-icon), removing part of the fork's original rationale. The GTK constraint remains (tao#509: "running webkitgtk outside of gtk is not fun or not even possible").
The drift. tao is frozen on winit's pre-0.30 closure API while winit's 0.31 redesign moved away from it (historically tracked in the now-closed winit#3367). Divergence can require parallel maintenance, but this audit did not establish that every platform fix is duplicated. Dioxus 0.7.9 still ships Tao/Wry; a winit migration is proposed in open dioxus#2706. Tauri v3 planning targets GTK4, not an un-fork. The structural fix, a windowing-agnostic wry (wry#1014) or winit GTK embedding, began landing just after the snapshot. Tauri's winit-gtk4 backend merged 2026-07-16 on winit's still-beta 0.31 architecture, and this audit found no dedicated public funding source for it.
Specific Linux paths in the common tauri-apps shell crates hit a GTK/X11 boundary:
muda's Linux menubar API requires a gtk::Window, so it cannot attach that menubar to a plain
winit window; tray-icon's winit path requires a second GTK event loop on a parallel thread; and
global-hotkey's Linux backend is X11-only. Wayland portal support already exists in Rust through
ashpd, while
ksni implements StatusNotifierItem. winit itself has no menu/tray API
(winit#403, open since 2018).
Why you care. On Linux a native-GPU Rust app often has no tray, no menubar on a plain winit window, and no Wayland hotkey. The work left is to wire the existing portal, SNI, and DBusMenu crates into a maintained winit-compatible facade. Every winit-based framework would get that for free.
What it is. Screen readers don't see pixels. They need a semantic tree ("a button named Add, focused"), and every OS has a different ancient API for it. AccessKit defines one Rust tree format with adapters for Windows UI Automation, macOS NSAccessibility, Linux AT-SPI, Android and iOS. No comparable cross-toolkit Rust abstraction was found in this audit; egui, Slint, xilem, Bevy, vizia, Blitz, GPUI (since May '26), and GTK 4.18 all integrate it, although Zed currently opts out of GPUI's path.
One active maintainer. The July snapshot said authorship was "dominated by Matt Campbell and Arnold
Loubriat"; a re-check of the repository on 2026-08-29 (after a maintainer's correction on Reddit) sharpens that. Since
mid-March 2026 Arnold Loubriat (DataTriny) has authored 42 of 58 commits,
merged all 54 pull requests and cut every release; Matt Campbell's last commit was 2026-03-04 and his last
merge 2026-03-15, though he still reviews (approved #756 on 2026-08-09) and remains a crates.io owner. Loubriat is the
only public member of the AccessKit org, and by his own bio does this
work in his spare time.
Funding. The Sovereign Tech Fund's 2023–24 GNOME programme paid Campbell for Newton and the GTK↔AccessKit
integration (GNOME STF report, "mostly wrapped up").
Since then there has been one institutional grant, which the July audit missed: an NLnet NGI0 Commons Fund grant,
"iOS support for AccessKit", started 2025-11 (project page) —
scoped and delivered as accesskit_ios 0.1.0 on 2026-05-11, not maintenance funding. The repository has no
FUNDING.yml; accesskit.dev's "Sponsor the project" link points at Campbell's
GitHub Sponsors (six sponsors, one of them a company), while Loubriat's
lists two individual sponsors and no corporate one.
Why you care. The ecosystem standardized on a crate with 5.9M recent downloads that one person maintains essentially alone, on their own time, with no project-level funding channel. The remaining gaps (a web/canvas adapter, deeper text-editing semantics, and the prototype-stage Wayland a11y stack "Newton") are long-running infrastructure work; funding them — and the maintainer — is recommendation 4 below.
The shared infrastructure under the sampled frameworks, ranked by how concentrated and how funded they look from public evidence. The table contains 21 entries; 15 are rated amber/red. "Author concentration" is
a heuristic derived from dated public commit attribution, not a measured probability of collapse, and "none found" means no public direct
funding source was identified, not proof that a contributor is unpaid. Exact reverse-dependency, sponsor, and search-result counts are dated
snapshot observations whose raw query responses were not retained. Full sourced table:
report/data/load-bearing-crates.md.
| Crate | What it is | Author-concentration indicator | Public support evidence | Last release | Observed signal / interpretation | Assessment |
|---|---|---|---|---|---|---|
| fontdb | finds fonts installed on the system | no sampled activity | no public direct source or announced successor found | Oct 2024 | 21-month release gap; used under Iced/COSMIC/Zed-Linux font resolution | red |
| apple-codesign | Rust-native signing & notarization from non-Mac CI | high | GitHub Sponsors (8 public at snapshot) | Nov 2024 | no release in 19 months and 69 open issues; principal open-source Rust-native path found | red |
| winit | opens windows, delivers keyboard/mouse/IME events everywhere | high | no direct institutional source found; personal sponsors visible | Mar 2026 | 0.31 redesign in beta for roughly 7 months and 21 days; a prolific contributor announced a hiatus citing burnout; 1,225 reverse deps recorded at snapshot | amber |
| taffy | CSS flexbox/grid layout math as a library | high | Dioxus Labs employs the lead maintainer full-time (since Mar 2024); no crate-level Sponsors listing | Jul 2026 | recent authorship concentrated in one maintainer under Zed, Bevy, Servo, Blitz, Slint | amber |
| accesskit | the screen-reader bridge for Rust toolkits (+ GTK 4.18) | high | 2023–24 STF/GNOME work wrapped up; NLnet NGI0 Commons grant (iOS, from 2025-11) scoped and delivered; no maintenance funding; 2 + 6 individual sponsors | Aug 2026 | one active maintainer (Loubriat: all PR merges and releases since Apr 2026); Campbell reviews only | amber |
| cosmic-text | text layout engine (the Iced/COSMIC one) | high | System76 employment supports the maintainers | Apr 2026 | active, with support concentrated in one company | amber |
| swash | rasterizes shaped glyphs into pixels | high | maintainer employment is separate from a direct project grant | Jun 2026 | recent public commits concentrated; compatibility work continues | amber |
| tao | Tauri's winit fork (GTK-hosted windows for Linux webview) | high | Tauri umbrella / CrabNebula involvement | May 2026 | recent authorship concentrated; remains on the pre-0.30 winit API | amber |
| muda | native menu bars & context menus | high | Tauri umbrella; no direct source found | Jun 2026 | recent authorship concentrated; Linux menubars require a GTK window | amber |
| tray-icon | system tray icons | high | Tauri umbrella; no direct source found | Jun 2026 | recent authorship concentrated; winit Linux path requires a parallel GTK loop | amber |
| global-hotkey | system-wide keyboard shortcuts | high / low-volume | Tauri umbrella; no direct source found | May 2026 | 6 commits in 12 months; its Linux backend is X11-only (ashpd exposes the portal separately) | amber |
| arboard | clipboard read/write | high | maintained in the 1Password organization | Aug 2025 | ~10.5-month release gap against 1,052 reverse deps at snapshot | amber |
| rfd | native open/save file dialogs | high | no public direct source found; 0 public sponsors at snapshot | Jan 2026 | active, with recent public work concentrated in one maintainer; 479 reverse deps | amber |
| notify-rust | desktop notifications | high | no public direct source found | Jun 2026 | active across three platforms, with recent authorship concentrated | amber |
| softbuffer | CPU pixel buffer onto a window (software rendering) | high | no public direct source found | Dec 2025 | 76 of roughly 84 commits in the audited year attributed to one author | amber |
| wgpu | one GPU API → Metal/Vulkan/DX12/WebGPU | lower | Mozilla employs core maintainers for Firefox WebGPU | Jul 2026 | broad employer-backed contributor set in this comparison | green |
| harfrust | the text shaper (ligatures, Arabic, kerning) | moderate | Google Fonts–backed, HarfBuzz org | Jul 2026 | active with upstream/institutional backing | green |
| parley + fontique | text layout engine + font fallback (the Linebender one) | lower | two active NLnet grants (through Aug 2026) + Canva contributor | Jun 2026 | active; time-bounded grant deadlines are a continuity consideration | green |
| wry | embeds the OS webview (the Tauri/Dioxus renderer) | lower | Tauri programme; CrabNebula employs a maintainer | May 2026 | multiple active contributors in the sampled window | green |
| tiny-skia | CPU 2D renderer (iced's software fallback) | lower | Linebender stewardship (post-handoff) | Feb 2026 | post-handoff release shipped; activity remains modest | green |
| raw-window-handle | the universal window-handle socket between crates | moderate | rust-windowing umbrella; no direct project source found | May 2024 | stable API with low release activity (78M downloads at snapshot) | green |
A todo app (text input, Enter shortcut, per-row delete, live counter, scrolling) built idiomatically in each
framework, versions pinned. All ten compiled and survived the launch check; later inspection observed their
windows, and the implementations cover the source-level spec (GPUI exposes low-level elements and a hand-built input
example but no reusable first-party high-level input widget, so this app approximated text input). On this M4 Pro, these ten small apps each clean-built in under a minute from a
warm registry and empty target directory; that is not a general large-application ranking. Floem is the one deviation from
the "latest crates.io release, pinned =x.y.z" rule: its published 0.2.0 was 20 months stale at measurement and main
cannot be published (it depends on a forked winit and on git-only understory_* crates), so all eight floem apps
pin git rev 778bb5f2. The unpublishable-main situation is itself a finding.
Round 2 tested interaction: drag-and-drop, live data, custom charts, hover
interactions, inline editing, animation. Two more apps per framework. "Pulse" is a live metrics
dashboard (draggable card grid, 1–60 Hz data at default 10, hover-tooltip chart) and "Board" is a kanban (cross-column DnD,
drop indicators, double-click edit). All 20 built and survived the scripted launch check, and later inspection observed
their windows (floem's two retained records are launch-liveness only); interaction evidence ranges from source/API paths through retained app tests as marked per capability.
Full data: report/11-interactive-results.md + per-app FRICTION.md.
| Capability | iced | egui | gpui | tauri | xilem | slint | dioxus | freya | vizia | floem |
|---|---|---|---|---|---|---|---|---|---|---|
| Charts + sparklines | hand-rolled | egui_plot | hand-rolled | Canvas2D | hand-rolled | Path strings | SVG in RSX | Skia canvas | vg::Canvas | canvas + kurbo |
| Chart hover + tooltip | hand-rolled | crosshair 1 line + snap code | hand-rolled | hand-rolled | hand-rolled | assembled | assembled | assembled | assembled | hand-rolled |
| DnD: grid reorder | hand-rolled | egui_dnd | native DnD | HTML5+math | hand-rolled | hand-rolled | hand-rolled | DragZone/DropZone | on_drag/on_drop | draggable |
| DnD: cross-column | hand-rolled | hand-rolled | native DnD | hand-rolled | hand-rolled | DragArea 1.17 | hand-rolled | built-in | built-in | built-in |
| Drag ghost | assembled | assembled | built-in | free (HTML5) | assembled | hand-rolled | hand-rolled | drag_element | hand-rolled | built-in |
| Live data 1–60 Hz | built-in² | idiom | spawn loop | events | task view | Timer | tokio DIY | async-io timer | cx.add_timer | exec_after chain |
| Inline edit (dbl-click) | assembled | assembled | own editor | assembled | dbl-click/Esc wrapper | assembled | assembled | press classifier | on_double_click | typed DoubleClick |
| Animate reorder/drop | snaps¹ | tweens | settle only | CSS FLIP | snaps¹ | animate x,y | CSS settle | scale-in on drop | CSS transition | spring release |
¹ The sample has no shared automatic FLIP/position-transition
abstraction: stock iced/xilem layouts snap, while the implementations used egui tweens, hand-built CSS FLIP, explicit Slint
x/y animation, or the framework-owned tweens and springs of the 2026-08-03 trio. Vizia comes closest, since a CSS
transition: height genuinely animates a layout property, so the kanban's insertion gap opens and closes. Even so,
no framework automatically transitions a card it did not itself move: Slint and floem animate positions only because the
app positions cards by index or the framework owns the drag ghost.
² iced's time::every needs a non-default executor feature; the compile error gives no hint.
plot/plotters feature was deliberately left unused to measure the drawing story), although current
Iced- and GPUI-compatible chart crates also exist with uneven maturity, as do DnD helpers (iced_drop supports
Iced 0.14 but was not discovered or evaluated during the iced implementation).EventsCombos::pressed, the
primitive its own text editor uses) that our first kanban missed and re-implemented with a timer
(corrected 2026-08-30 after the Freya maintainer pointed it out; the Escape plumbing is still hand-rolled).event::listen; the gap is an on_escape hook, iced#2678), vizia only firing actions on the hovered entity (so a click on a card's own label silently does nothing — a regression of vizia#406, filing), and freya canvases that never repaint unless they carry an event handler (confirmed on main — filing). Upstream status checked 2026-08-30. The retained list does not establish one trap for every framework.One quick-note app per framework exercising everything AROUND the window: tray, global hotkey, native
menubar, dialogs, clipboard (text + image), Finder file-drop, notifications, live dark mode, multi-window, close-to-tray.
Zero implementation-level not-achievable cells on tested macOS. All ten frameworks exposed a viable path,
although Finder drops and some notification results were source/API-level rather than real end-to-end observations.
Full matrix + evidence notes: report/12-shell-integration-results.md. The round's traps and reference shapes
are distilled into a facade requirements brief: the winit shell facade, specified.
| Capability | iced | egui | gpui | tauri | xilem | slint | dioxus | freya | vizia | floem |
|---|---|---|---|---|---|---|---|---|---|---|
| System tray | assembled | assembled | assembled | built-in | assembled | DSL element | built-in | tray feature | tray-icon | tray-icon |
| Global hotkey | assembled | assembled | assembled | plugin | assembled | assembled | built-in | assembled | assembled | assembled |
| Native menubar | muda¹ | muda¹ | built-in | built-in | muda¹ | DSL element | built-in | muda | muda | built-in |
| Native dialogs | rfd | rfd | built-in | plugin | rfd | rfd | rfd | rfd | rfd | built-in |
| Clipboard image | arboard | arboard | built-in | plugin | arboard | arboard | arboard | arboard | arboard | arboard+png |
| File drop (Finder) | built-in | built-in | built-in | built-in | workaround | unstable feature | built-in | built-in | built-in | built-in |
| Notification | notify-rust | osascript² | notify-rust | plugin | osascript² | notify-rust | notify-rust | notify-rust | notify-rust | notify-rust |
| Dark mode (live) | built-in | built-in | built-in | built-in | hand-rolled | built-in | built-in | built-in | built-instartup-observed; live toggle not run | built-in |
| Close-to-tray | built-in | assembled³ | assembled | built-in | assembled | built-in | built-in | assembled | assembled | built-in |
¹ A trap reproduced in these 3 integrations: muda's
predefined Edit items silently swallow ⌘X/⌘C/⌘V before the framework's own bindings, so the standard Edit menu breaks paste
app-wide. Freya, vizia and floem all reach the same muda API and all three deliberately avoided the predefined Edit
roles for this reason. ² notify-rust was rejected for different reasons in the two frameworks: egui hit a frame-scheduling
freeze in two of three runs (cause unproven; a 2026-08-30 source check rules out the earlier delegate-replacement hypothesis and points at the helper's synchronous run-loop pumping; no minimized reproduction was retained), while xilem hit AppleScript's blocking app chooser for the use_default bundle id, a winit re-entrancy panic-abort, and silent no-banner delivery from a
detached thread. ³ eframe never runs App::ui for hidden viewports, so reopen logic must
live elsewhere or the window can't come back. Round upset. dioxus quietly matches Tauri here (tray,
hotkey, menubar, multi-window, close-to-tray all built-in). 2026-08-03 addendum. floem is the only
native-rendered framework here with native menubar, dialogs, file-drop, live dark mode, multi-window and Dock-icon
reopen all built in, none of it documented outside its source. freya's tray feature owns the global
tray-icon/muda handlers itself, which structurally prevents the muda channel-splitting trap dioxus hit, a trap iced and
egui avoided only by deliberately routing both menus through the tray_icon::menu re-export, and floem escaped
by accident via a muda version mismatch; vizia ships no
shell layer beyond windowing, but its models run on the main thread with no Send bound, so the !Send
tray handle just lives in app state. Scope note. This is macOS; the Linux GTK
fault line (muda menubars cannot attach to plain winit windows; global-hotkey is X11-only) still holds as documented in the fragmentation stories.
The same multilingual corpus (Latin ligatures, Arabic/Hebrew BiDi, Chinese/Japanese/Korean, Devanagari,
Thai, ZWJ emoji, one all-scripts line) rendered by every framework.
Verification used screenshots plus OCR/glyph/caret diagnostics described in each FRICTION file. All ten screenshots are
retained, but not every raw diagnostic output is; exact caret/OCR/mark counts and caption-specific interpretations therefore
remain local observations rather than independently reproducible results. Full matrices incl. evidence levels and editing
behavior: report/13-text-i18n-results.md.
The three stacks added on 2026-08-03 are not embedded
above (page weight); their window-scoped captures are retained at apps/freya-babel/screenshot.png,
apps/vizia-babel/screenshot.png and apps/floem-babel/screenshot.png. Verdicts:
freya (Skia textlayout over CoreText) renders all 11 corpus lines, no tofu, 0 bytes of bundled fonts;
vizia (SkParagraph over CoreText) also renders all 11 lines, no tofu, 0 bytes bundled;
floem (parley/fontique) is the second stack in the corpus to hit the fontique Han-discovery hole xilem
exposed. Han and kana ([ZH], [JA], and 世界 inside the mixed line) render as pure tofu because fontique 0.7.0 (floem's pin) rejects CoreText's PingFang answer, which has no readable font file on macOS 26, while Hangul, Devanagari, Thai and BiDi are all correct; regional-indicator flag pairs are tofu too (a harfrust AAT shaping bug). Both are fixed upstream in fontique/parley 0.8.0 (2026-03), so the fix is a dependency bump in floem, not app code (status checked 2026-08-30).
Editing is the sharper finding.
Rendering mostly works. Backspace over 👨👩👧👦 deletes the whole cluster in WebKit and gpui's hand-rolled editor, shrinks it
through valid smaller emoji in Slint (by design), and splits the cluster or leaves a dangling zero-width joiner in the tested
iced, egui, and xilem editing paths. Those integrations lacked whole-cluster deletion out of the box. Source re-check 2026-08-30: cosmic-text 0.15's Backspace deletes one char while its Delete uses grapheme clusters (unreported — we are filing it); parley 0.6's backdelete treated one char as a cluster and is fixed on main by PR #715 (unreleased); egui deletes per char by design. The 2026-08-03 trio splits the same way from three more stacks:
floem's Lapce editor core treats the 25-byte family as a single caret stop (the best grapheme behaviour measured here),
vizia moves and selects by grapheme but deletes by scalar (its emoji state machine never advances its cursor — a one-line fix, unreported, we are filing it), and freya-edit 0.4 moves the caret by UTF-16 code unit, so a caret can land inside a surrogate pair and one Backspace corrupts the cluster and leaves a dangling joiner, in a crate that already depends on unicode-segmentation (fixed upstream in freya-edit 0.5.0-rc.1, PR #2034). Egui also dropped 13 corpus combining marks; Slint showed one notdef mark and
clipped tall stacks. macOS AX selected-text queries returned garbled RTL text for egui; actual screen-reader speech was
not exercised, so assistive-technology impact remains an inference.
The seven July iteration-1 apps were bundled into launching DMGs containing ad-hoc-sealed .app bundles. The
"packaging is the hard part" claim splits in two. The mechanical layer is nearly free. A revived
cargo-bundle packaged all six non-Tauri frameworks with an
identical 4-line stanza, under a minute each, and it beat
cargo-packager, the ecosystem's "intended answer,"
head-to-head in this run. The trust layer is the real cliff. With no identity configured, none of the
tested runs produced a distribution signature. cargo-bundle lacks signing, while Tauri/cargo-packager support it when
configured. The create-dmg flows failed repeatedly on this macOS 26 machine, while a plain hdiutil create
fallback was more reliable. In the standard quarantined-distribution path tested here, spctl rejected all seven
of those ad-hoc bundles as expected without Developer ID signing and notarization; Tauri,
cargo-packager, and the principal Rust-native cross-platform implementation
rcodesign automate parts of that workflow but cannot supply
Apple credentials. Bundle sizes: 5.0 MiB (GPUI) → 14.7 MiB (Slint).
Reliable app-attributed notification banners and dock behavior also benefit from a bundled,
identified .app. 2026-08-08 completion. The current freya, vizia and floem apps went through the identical
cargo-bundle pipeline. All three launch, seal, and hdiutil-verify (spctl rejected as expected); floem, packaged
for the first time, is smaller than the Skia-static pair (17.1 vs 20.3/21.7 MiB .app). The same day, an RCN-thread
tip was tested: dx bundle (dioxus-cli 0.7.10) packaged all nine non-Tauri apps 9/9 from a 4-line
Dioxus.toml each, and its built-in DMG step succeeded first-try for all nine, the exact step that failed on
6 of 8 tool paths in July. Costs: it rebuilds each app under its own desktop-release cargo profile (~24–53 s) and ignores
[package.metadata.bundle]. Its macOS signing/notarization automation exists in the 0.7.10 code but was not
exercised (no credentials, by design). Details: report/14-packaging-results.md + report/data/packaging-results.md.
Round 4 tested the last untested dimensions: "Peek" (camera preview ≈30 fps, mic meter,
audio, 200-image gallery), "Grid" (100k rows: virtualization, sort, filter, resize, selection), and
"Fetcher" (debounce, streamed progress, and cancellation proven by a local server that logs client
disconnects). All 30 apps built and ran. Full matrices with per-cell evidence labels for the original seven:
report/15-media-hardware-results.md,
report/16-data-grid-results.md, and
report/17-async-network-results.md; the freya/vizia/floem
evidence lives in report/data/iter4-rows.md and the per-app FRICTION.md files.
ps samples in their
self-test logs). *egui at 720p; iced/slint/xilem/freya/vizia/floem at 1080p. floem's figure is not comparable: its
vger renderer keys images by content hash in a single atlas, and any frame above roughly a third of the atlas dimension
wedges image drawing permanently (the preview goes black after ~3 frames), so every frame is downscaled to ≤320×180 on
the camera thread and the atlas is cleared roughly every 13 frames. Tauri also deviated from
the requested cpal/rodio path by using WebAudio/getUserMedia. Dioxus's Rust-side alternative measured 4–6× its JS path for identical pixels.VirtualTable is genuinely virtualized (22 of 100,000 rows
materialized at every scroll ratio), sortable, resizable and selectable, though its own row/cell wrappers eat the
click that selects a row until an undocumented CSS class is given pointer-events: none. Everywhere else
the table is assembled: egui_extras TableBuilder was the only other suitable first-party basis (resizing built in,
sorting and shift-selection assembled in the app); iced's new table eagerly built 600k cell widgets in the
inspected path; Slint's StandardTableView did not meet the chip or multi-selection parts of this spec; freya ships a
Table that is a layout helper (one element per cell, 600k at this scale) and a separate
VirtualScrollView that is the real answer; floem has no table widget at all. Stock virtualization exists
in gpui (uniform_list), slint (Model/ListView), xilem (whose cited run
scrolled roughly 8,000 rows deep rather than the full 100,000, a scroll-depth note rather than a capability limit),
freya and floem; xilem's and floem's table furniture was hand-rolled.TaskHandle drops the future
at its await point. That one mechanism is debounce, stale guard and protocol-level cancellation.tokio feature); ehttp's with_timeout flag is ignored for GET and inverted for POST, so streaming GETs are silently capped at 30 s (corrected 2026-08-30 — its default timeout does not kill an 8 s stream); gpui ships an HttpClient trait with no published transport; tauri's webview
fetch is CORS-blocked from tauri://localhost (the plugin fix bakes URL scopes at build time); xilem re-exports
tokio without macros; slint's spawn_local has an executor but no reactor (documented; use async_compat); dioxus's occluded windows park
the whole task loop (3/6 ordinary runs, deterministic with always-on-top; 0.7.9). In-flight downloads stop because dioxus-desktop waits for the webview's JS to ack each edit batch before polling any Rust future; wry has the throttling knob, dioxus doesn't plumb it (tracked as dioxus#5586 / PR #5587).
The 2026-08-03 trio adds three more shapes: freya has an executor but no interval hook (only a one-shot use_timeout behind a non-default feature) (five of its eight apps
depend on async-io purely for Timer::after); vizia has no executor whatsoever, so every networked app
re-invents the same thread + ContextProxy bridge, though there is also no executor mismatch to get wrong;
floem likewise ships no executor, yet its ExtSendTrigger/create_ext_action pair is the
cleanest foreign-thread wakeup measured here and it is the only framework with a built-in debounce_action.min_height: auto lets a scroll grow to min-content, which silently disables VirtualStack virtualization
(100k rows → 16 GiB RSS and no window ever appears), and vger's content-hash atlas wedges image drawing permanently
above ~320×180. One obscure style line fixes the first; nothing warns about either.The macOS-built apps, rebuilt and run on Linux headlessly (lavapipe software Vulkan + llvmpipe GL,
WebKitGTK 2.50.6, one plausible CI configuration). All 11 tested apps compiled with zero source
changes (gpui needed one -dev system package at link time); the differences live mainly in the render
path and the shell layer. Font discovery/fallback and gpui's present path also differ per platform. This round predates
the expansion: freya, vizia and floem have not been built or run on Linux. Verbatim logs, screenshots, and
probe sources retained. Full report: report/18-linux-reality-results.md.
WGPU_BACKEND=gl rescued both wgpu failures.The 80 apps, rebuilt and launch-probed on one real Windows 11 x64 laptop (MSVC toolset pinned to 14.34,
200% DPI, WebView2 151.0.4129.59). 28 of 80 apps fail cargo build --release as-is, including
three entire frameworks at 0/8 (tauri, freya, vizia), all three on packaging/distribution infrastructure rather than
framework Rust code. 36 reached a visible window on the default env, and WGPU_BACKEND=dx12
rescued all 14 of the intermittent wgpu surface deaths. Verbatim logs, per-variant runs, screenshots and CSVs
retained. Full report: report/21-windows-reality-results.md.
FailedToCreateSurfaceForAnyBackend (empty per-backend error map — instance-level, not provably Vulkan surface creation) (floem 0/6 on
default). WGPU_BACKEND=dx12 rescued all 14 surface deaths.icons/icon.ico before any app code compiles (the icon is missing from our repo — our omission, not a tauri defect); freya 0/8 because its prebuilt-Skia download died on curl(3) on this machine (the Windows asset does exist upstream and downloads elsewhere — cause unisolated; corrected 2026-08-30) and the source fallback demands LLVM; vizia 0/8 on LNK1120, five unresolved __std_* externals linking skia-safe's
prebuilt skia.lib against the pinned 14.34 MSVC STL.core-foundation/objc unconditionally instead of under a macOS target table (our gating bug, not a gpui limitation — corrected 2026-08-30).AlreadyRegistered (Win+Shift+9 is an OS taskbar shortcut held on this desktop). egui-tray and xilem-tray
.unwrap() and die; the other five log the failure and keep running. iced-tray captured the campaign's only
tray screenshot; dioxus-tray's notify-rust show() returned Ok from a bare unsigned exe (no toast was captured, so that is not evidence of display).tray feature that owns the global tray-icon/muda handlers, and the cleanest async story here: a single-threaded executor on the UI thread, so await then assign to a signal, no channels and no Send.Input is hard-wired to one line, there is no interval hook (only the one-shot sdk::use_timeout behind a non-default feature; live data is a bare spawn plus your own async-io timer), double-click is a classifier call (EventsCombos::pressed) rather than an event, and freya-edit 0.4 moves the caret by UTF-16 code unit, so one Backspace can split a ZWJ emoji cluster and leave a dangling joiner (fixed upstream in freya-edit 0.5.0-rc.1).Model::event, on winit + Skia, plus the only real CSS engine in the sample (stylesheets, transitions, @keyframes). Smallest dependency tree and fastest builds of the three additions, and the only framework here whose data grid is a view constructor: VirtualTable is virtualized, sortable, resizable and selectable in core. Models have no Send bound and run on the main thread, which is where !Send OS handles live comfortably.ExtSendTrigger foreign-thread wakeups.main is structurally unpublishable, so every consumer inherits the fork and the churn. Rules itself out where accessibility is required, and at this rev fontique 0.7 renders Han and kana as tofu on macOS (fixed upstream in fontique 0.8.0 — a bump away) while vger's image atlas wedges above ~320×180.Two specs an r/rust commenter asked for: "Windows" (what is a window — modality, parenting,
shared state across windows, close veto, persistence) and "Ledger" (can the text input be a numeric input —
typed filtering, locale, decimal alignment, tab order, undo — plus the corpus' first verified accessibility-tree dump). All 22 apps
self-test (SELFTEST DONE … fail=0) and were re-verified by three independent passes. Full write-up:
report/22-windows-forms-results.md; normalized 11-column matrices: report/data/verification/iter5-matrix.md.
Signal per VirtualDom via
with_root_context (works — while GlobalSignal silently forks per window); eframe's deferred viewports force
Arc<Mutex>; slint's export global is per-component-instance.window.prompt and tauri's set_enabled(false)
(a beginSheet: disabler under the hood); iced/slint/floem reached the same sheet with ~30 lines of objc2; everyone else
ships an overlay that blocks input but is not OS modality. winit has no modal API, no cross-platform owner windows
(with_owner_window is Windows-only), and addChildWindow: crashes freya (AccessKit adapter ordering) and
fights tauri's always_on_top.key-pressed → accept), freya (on_validate) and iced (controlled input); vizia can only
post-validate; dioxus drops fast keystrokes (DOM→IPC→VDOM round trip) and needs a remount to reject one. Locale parsing was
hand-rolled in 10 of 11 — only the webview got Intl.NumberFormat for free. tnum tabular figures are
reachable only in the CSS engines and gpui (measured: 63.44 px vs 86.68 px for ten digits).app.rs runs on it
unchanged (a #[path] include and a swapped platform.rs — the Ledger crate's own Rust is 78 lines),
in one process instead of four — at 24.5–27.9 MB binaries vs 6.5 MB, 60–69 s clean builds, and with beta gaps:
<select>/<input type=date|range> paint nothing, Tab emits no focus events, ⌘-chords never reach
the VirtualDom — but it re-implemented its launcher and gained a genuine close veto the webview lacks. Serial measurements:
measurements/reruns/20260830T193713Z/results-iter5.csv.
accesskit anywhere in its dependency tree. Integration presence is not audited screen-reader compliance, so prototype with VoiceOver/NVDA first.Whatever you pick, prototype the riskiest screen first (not hello-world), pin versions exactly (several audited tutorials were stale; this was not universal), test Linux on Wayland + NVIDIA early, and budget real time for packaging, signing, and updates. Tauri has the most integrated first-party chain; other stacks assemble maintained bundlers, platform tools/rcodesign, and Velopack.
report/19-shell-facade-spec.md).