FlutterWasmWeek: benchmarked dart2wasm vs dart2js on a CHIP-8 emulator — 2.6–4.4x, plus a benchmarking trap

Since the Flutter team is collecting wasm feedback this week, I wanted numbers where the delta is attributable to codegen alone, not the rendering pipeline. So I wrote a CHIP-8 emulator in pure Dart: interpreter hot loop over Uint8List, zero allocations in the loop, framebuffer blitted in a single CustomPainter. Same source compiled twice — flutter build web vs flutter build web --wasm. Flutter 3.47, both release, same Chrome.

Results:

- dart2wasm: ~130M emulated cycles/sec, and the same number in every environment I tried (interactive window, headless, occluded tab)

- dart2js: 30–51M depending on environment, with a few seconds of JIT warmup before reaching full speed

- speedup: 2.6–4.4x. Migration cost for this codebase: one build flag, zero code changes.

Two things surprised me:

  1. The consistency matters more than the multiplier. Wasm runs at full speed from the first frame, everywhere. JS swings by 70% between environments. For anything latency-sensitive, that predictability is the real win.

  2. A trap if you benchmark this yourself: if your metric is cycles per wall-clock second, Chrome's requestAnimationFrame throttling (occluded or backgrounded window) silently crushes the number ~4x. I chased that ghost for a while. Normalize by actual compute time, or you're measuring the compositor, not the compiler.

Caveats: one compute-heavy workload, one machine. UI-bound apps will see much smaller gains, and packages relying on legacy dart:html / dart:js interop can block the wasm build entirely — that's the first thing to check on a real codebase.

(It also plays the original 1990 Pong ROM, which was not strictly necessary but was the most fun part.)

Happy to answer questions or share details about the setup.

reddit.com
u/alex-bordei — 2 days ago

icefelix_window_manager — cross-platform window management for Flutter desktop (macOS + Windows + Linux in one package)

Published icefelix_window_manager to pub.dev — a single Flutter plugin that handles window management across all three desktop platforms. macOS (Swift + AppKit), Windows (C++ + Win32), and Linux (GTK 3 + X11/Wayland) all ship in one package.

Links:


What you can build with it:

  • Custom title bars (frameless windows like Spotify / Slack / Discord) via setFrameless + setTitleBarStyle(hidden) + startDrag on your own draggable region
  • "Unsaved changes — are you sure?" on Cmd+Q / Alt+F4 via WindowCloseRequestEvent.preventDefault() — sync, idempotent, works on all three platforms
  • Persist window size + position between launches — subscribe to snapshot once, write to SharedPreferences, restore on next start with setBounds
  • Multi-monitor — detect displays, move your window to a second screen, react to hot-plug via displays.list() + moveToDisplay() + displays.events
  • Always-on-top utility windows (calculator, color picker, sticky notes) via setAlwaysOnTop
  • Fullscreen presentation mode with proper enter/exit
  • Glassmorphism / translucent windows via setOpacity + setBackgroundColor with alpha
  • Non-rectangular polygon windows via setShape(points) — true non-rectangular hit-testing on Windows (clicks pass through to the desktop outside the shape). Here's 10 polygon-shaped Flutter windows running side-by-side
  • Adaptive layouts — listen to WindowResizeEvent instead of polling MediaQuery

Quick start:

dependencies:
  icefelix_window_manager: ^0.4.0
await WindowManager.instance.ensureInitialized();
WindowManager.instance.events.listen((event) {
  switch (event) {
    case WindowResizeEvent(:final newSize): print('resized to $newSize');
    case WindowCloseRequestEvent():
      if (unsavedChanges) event.preventDefault();
    // compiler errors if you miss a case
  }
});

What's different from existing window plugins:

  • Reactive snapshot as single source of truth. WindowManager.instance.snapshot is a ValueListenable<WindowSnapshot> that updates atomically when anything changes — your setter calls AND external mutations (user drags, monitor plugged in). All fields update together, no partial states.

  • Sealed event hierarchies for Dart 3. WindowEvent covers Resize, Move, Focus, StateChange, DisplayChange, CloseRequest. DisplayEvent covers Added/Removed/Changed. Exhaustive switch — the analyzer tells you if you forget a case.

  • Sync preventDefault() close interception — hooks into each platform's native close handler (windowShouldClose: on macOS, WM_CLOSE on Windows, delete-event on Linux) via delegates that preserve Flutter's own delegate chain.

  • Multi-monitor with stable IDsCGDirectDisplayID on macOS, HMONITOR adapter on Windows, GdkMonitor manufacturer|model on Linux. Not array indices.


Platform status (honest):

Feature macOS Windows Linux
All setters/getters Yes Yes Yes
Close interception Yes Yes Yes
Multi-monitor + hot-plug Yes Yes Yes
setShape (polygon windows) Visual only Full (hit-test) No-op (planned)
setMovable Full Full Flag-only
setAlwaysOnTop Full Full Best-effort (Wayland)

Wayland's "no window position" reality is honored — snapshot.bounds.position is nullable by design, not a workaround.


What's NOT in scope:

Tray icons, system menus, dock badges — those will live separately if there's demand. This plugin is specifically window management.


Feedback, issues, and PRs welcome. If you hit a bug or need a feature, open an issue — I actively maintain this and ship fixes fast: https://github.com/ICE-Felix/icefelix-window-manager/issues

License: BSD-3-Clause.

u/alex-bordei — 3 months ago