You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Overall, the new WebView with the Tao backend is very good!
However, we’ve noticed a noticeable performance degradation compared to previous versions (at least on Mac). It’s not as smooth as before, especially in WebGL games or maps. It definitely no longer renders at 120 FPS.
Is this caused by offscreen rendering costs, or are there internal settings that limit the frame rate to around 30 FPS? If so, could the max_fps API be made public?
Thanks for the detailed report. Short answer: there is no offscreen rendering and no frame-rate cap in the library, so there is no max_fps to expose — but I could not reproduce the slowdown either, so I would like your numbers before closing this.
What the backend actually does
The desktop backend embeds a real native view, it never renders the page into a bitmap:
macOS: the WKWebViewNSView is a subview of the Tao window, below the Compose Metal layer, which punches a transparent hole over the WebView rect
Linux: the WebKit2GTK widget is reparented into Tao's content widget
Windows: WebView2 runs as a DirectComposition visual composited by DWM
No backend contains frame-rate logic (grep for fps/frame/vsync/timer over webview-compose/src/jvmMain/native/** is empty). Pacing is the platform compositor's job.
Measurements (macOS, M4, 90 Hz display, Nucleus Tao 2.5.5)
Embedded WebView vs. the same page in a bare WKWebView in a plain NSWindow:
Workload
Embedded (Tao NativeView)
Bare WKWebView
Canvas 2D rAF
90 fps
90 fps
WebGL (light)
90 fps
90 fps
WebGL, GPU-bound shader
31–34 fps
32–35 fps
Compose rendered at 90 fps in the same window at the same time. Blending was the obvious suspect, so I measured it separately at full-screen size (2560×1040) with an animated Compose overlay composited over the WebView every frame — page fps / Compose fps, plus GPU utilization while both run at the display rate:
Compose overlay
Light page
GPU-bound page
GPU util (light page)
none
90 / 90
34 / 90
23.4 %
64 dp animated bar
90 / 90
34 / 90
—
full-window translucent scrim
90 / 90
34 / 90
22.9 %
full-window opaque surface
90 / 90
34 / 90
—
bare WKWebView, opaque window
90
34
27.6 %
No measurable blending penalty. But my display is 2560×1040 @1x at 90 Hz on a desktop M4 (~2.7 Mpx/frame); a Retina ProMotion laptop is ~7.7 Mpx at 120 Hz, roughly 4× the compositing bandwidth and no fan — so this does not rule out your case, it only says I cannot see it here.
Could you run the diagnostics? (v1.0.2)
The visual e2e suite now reports two rendering measurements — ./gradlew :e2e-desktop:run, then look for:
The R01 / R02 values (R01 should be ~120 fps there; R02 must name a GPU — a software renderer would explain slow WebGL immediately)
The same numbers in your own app, and whether Compose content overlaps the WebView
The display refresh rate, and whether the window is on the internal Retina display or an external one
One trap worth ruling out first: R01 is reported as Skipped when the document is hidden. Every engine suspends requestAnimationFrame for a fully covered or backgrounded window — I hit exactly that during this investigation and a bare WKWebView behaves identically. So keep the window in front while measuring.
Also relevant if you are on battery: an opaque Compose overlay does not stop the WebView underneath — it keeps rendering at full speed behind it, so the GPU work is duplicated. Hide or dispose it rather than covering it.
Details and how to reproduce: README section "Rendering model & frame rate" (#58).
Thank you for your detailed answer!
Since this turned out to be a native view (perfect!) the cause is most likely in some other changes in our code. We didn't perform one-to-one comparison so this was just an impression (possibly wrong based on your explanation).
You may close this issue for now I think. If we get some updates on this (or some actually measured numbers) I will reopen it. Thanks again!
Activity
Add rendering frame-rate diagnostics and bump Nucleus to 2.5.5 (#58)
kdroidFilter commentedon Aug 30, 2026
Thanks for the detailed report. Short answer: there is no offscreen rendering and no frame-rate cap in the library, so there is no
max_fpsto expose — but I could not reproduce the slowdown either, so I would like your numbers before closing this.What the backend actually does
The desktop backend embeds a real native view, it never renders the page into a bitmap:
WKWebViewNSViewis a subview of the Tao window, below the Compose Metal layer, which punches a transparent hole over the WebView rectNo backend contains frame-rate logic (
grepfor fps/frame/vsync/timer overwebview-compose/src/jvmMain/native/**is empty). Pacing is the platform compositor's job.Measurements (macOS, M4, 90 Hz display, Nucleus Tao 2.5.5)
Embedded WebView vs. the same page in a bare
WKWebViewin a plainNSWindow:NativeView)WKWebViewrAFCompose rendered at 90 fps in the same window at the same time. Blending was the obvious suspect, so I measured it separately at full-screen size (2560×1040) with an animated Compose overlay composited over the WebView every frame — page fps / Compose fps, plus GPU utilization while both run at the display rate:
WKWebView, opaque windowNo measurable blending penalty. But my display is 2560×1040 @1x at 90 Hz on a desktop M4 (~2.7 Mpx/frame); a Retina ProMotion laptop is ~7.7 Mpx at 120 Hz, roughly 4× the compositing bandwidth and no fan — so this does not rule out your case, it only says I cannot see it here.
Could you run the diagnostics? (v1.0.2)
The visual e2e suite now reports two rendering measurements —
./gradlew :e2e-desktop:run, then look for:What would help most, from your ProMotion Mac:
R01/R02values (R01should be ~120 fps there;R02must name a GPU — a software renderer would explain slow WebGL immediately)One trap worth ruling out first:
R01is reported as Skipped when the document ishidden. Every engine suspendsrequestAnimationFramefor a fully covered or backgrounded window — I hit exactly that during this investigation and a bareWKWebViewbehaves identically. So keep the window in front while measuring.Also relevant if you are on battery: an opaque Compose overlay does not stop the WebView underneath — it keeps rendering at full speed behind it, so the GPU work is duplicated. Hide or dispose it rather than covering it.
Details and how to reproduce: README section "Rendering model & frame rate" (#58).
mtpdog commentedon Aug 30, 2026
Thank you for your detailed answer!
Since this turned out to be a native view (perfect!) the cause is most likely in some other changes in our code. We didn't perform one-to-one comparison so this was just an impression (possibly wrong based on your explanation).
You may close this issue for now I think. If we get some updates on this (or some actually measured numbers) I will reopen it. Thanks again!