Skip to content

Brewser v0.1.120 - Video Streaming Engine + Live-DOM Surface Push

Latest

Choose a tag to compare

@natureglass natureglass released this 07 Sep 17:43
· 1 commit to main since this release

Brewser logo

Brewser is a homebrew web runtime and app platform for the Nintendo Switch, a real V8 + Skia + WebGL2 engine running natively on the Tegra X1, built for the kind of web apps Chrome can't reach. Catalogue and submissions at brewser.io

✨ Highlights

  • Hardware-accelerated video streaming - async decode, in-shader YUV→RGB, zero-copy delivery, A/V decouple, and a content-fps-aware present gate. 720p60 now holds a steady 60 fps on device.
  • In-app video player - app-drawn control bar with loading placeholder, play/pause glyph, tap-to-toggle, and a fullscreen path.
  • Live-DOM engine leap - a ~5,300-line rendering push plus complete CSS @keyframes, vertical margin collapsing, and border-collapse support.
  • Update & compatibility UX - new-version boot toast, in-app version notification, minBrewserVersion compatibility notice, and an Offline Mode toggle.
  • Emoji atlas refresh - the full bundled emoji set re-rendered at higher resolution.

📺 Video Engine (native / nx.js core)

The <video> path was rebuilt from decode to present. All items below are HW-validated (2026-09-06).

  • Async decoder open - VideoDecoder now opens off the V8 main thread, so starting a stream no longer freezes the shell.
  • In-shader YUV upload - the decoder emits planar I420 (1.5 B/px) and Skia does YUV→RGB on the GPU, cutting per-frame texture upload ~2.6× while keeping full 720p.
  • BGRA-direct path - drops the per-frame double swizzle for BGRA sources.
  • Zero-copy frame delivery - frames wrap the decoder's buffer instead of a per-frame ~1.4 MB malloc.
  • A/V decode decouple - single-thread demux-ahead with per-stream packet deques and non-blocking video enqueue; audio is drained to a 0.25 s target so a full video ring can never starve audio (aur_underruns held at 0).
  • GC-hitch fix - the YUV delivery path now reuses one persistent ArrayBuffer (deliver_ab) instead of handing V8 a fresh one each frame. This eliminated the 720p periodic hitch: major GC was firing 2–3×/s (~7.5 ms each) against the 33 ms budget; it now drops to majGC = 0.
  • Content-fps present gate - VideoDecoder.fps is now exposed. The 30 Hz present lock (added to smooth 30 fps content) was halving 60 fps content to 30 shown fps; the shell now keeps 60 Hz for >32 fps content.
    • HW results: 720p60 → steady 60 fps; 480p60 → ~53–60; 4K60 → ~51 (decode ceiling).
  • gfxSetSwapInterval() binding - EGL swap-interval pacing lever (the 30 Hz lock is left in place but flag-gated after HW proved a slow present starves the single decode thread's audio).

🎬 Video Player (page runtime + shell)

  • YUV-mode <video> consumer via Switch.createBitmapFromYUV, with a VIDEO_USE_YUV kill-switch and frame-bitmap reuse.
  • App-drawn control overlay: fixed-video blit, "Loading stream" placeholder, paused play-glyph, above-composite repaint.
  • Tap-to-play/pause on the video frame in non-fullscreen mode.
  • Shell publishes active-decoder content fps (getActiveVideoContentFps()) to drive the 60 Hz gate; new player UI assets bundled (play/pause/volume_on/volume_off/fullscreen/gear).

🧬 Live HTML / CSS / DOM Rendering

  • Major live-DOM update - ~5,300-line expansion across the HTML parser, live-css, live-dom, live-layout, live-overlay, inline-css, live-form, and the SVG painter (+1,457 lines).
  • Complete CSS @keyframes (HW/Citron-confirmed) - adds translate (px + %), background-color/color interpolation, animation-delay, and animation-direction (normal/reverse/alternate/alternate-reverse) on top of the existing rotate/scale/opacity + easing.
  • Vertical margin collapsing (CSS 2.1 §8.3.1) (HW/Citron-confirmed) - adjacent-sibling, parent↔first/last-child collapse-through, and empty-block self-collapse, correctly gated by BFC/border/padding edges. Also fixes a pre-existing bug where padded/bordered containers dropped their last child's margin.
  • border-collapse: collapse (HW/Citron-confirmed) - shared table borders drawn centred on cell edges so adjacent borders coincide instead of doubling.
  • Live CSS + emoji atlas - path-utils, canvas-runner, emoji-atlas, and permission-policy refinements.

🔔 Updates, Compatibility & Offline

  • New-version boot toast - boot update check surfaces available updates at startup.
  • In-app version notification and refreshed Check for Updates / missing-app modal flows.
  • minBrewserVersion compatibility notice - the app modal shows a non-blocking amber notice when an installed app expects a newer Brewser, sourced from the installed manifest first (falling back to the catalogue).
  • Offline Mode toggle - one __brewserOfflineMode global gates the boot reachability probe, version poll, and telemetry; catalogue reads from cache, the connectivity indicator shows orange, and network-bound controls (Check for Updates / Download) are disabled. Applies live without a reboot.

🔐 Permissions & Compatibility

  • Permission warnings catalogue (warnings.json) - per-permission risk level + user-facing warning copy for network, local_network, storage, system, filesystem read/write, device_info, account, external_links, USB, Bluetooth, and NFC.
  • Surface compatibility push - shell + navigation fixes broadening general web-page compatibility.
  • Added a local-network classifier test suite (91 assertions).

😀 Assets

  • Full bundled emoji atlas re-rendered at higher resolution (~360 glyphs, including flags and ZWJ sequences).

🔐 Checksums

brewser.nro: 26ea567f10188f6938625dbedd0cebd29363afae25f78b7b4211f43738c3f629
brewser-forwarder.nsp: 1e3268fe83d45a811c38366645ed59854d6a0a4243de9c97ab156ffdc1a33af8

🙏 Credits

Brewser stands on the shoulders of nx.js by TooTallNate, Atmosphère, V8, Skia, Mesa/Nouveau, and libuv - and on the wider Switch homebrew community.

⚖️ Disclaimer

Brewser is a homebrew application for consoles running custom firmware. It is not affiliated with, endorsed by, or associated with Nintendo. Use CFW at your own risk.


Found a bug? Open an issue · Built something cool? Submit it at brewser.io/submit · Say hi on the GBAtemp thread