Update on what happened in WebKit in the week from March 2 to March 9.
As part of this week's handful of news, WebKitGTK and WPE WebKit
now have support for Gamepad's "VibationActuator" property, the
video decoding limit is now configurable at runtime in addition
to build time, and an interesting fix that makes WebKit render
fonts like other browsers by making it blend text incorrectly (!).
With these changes, playEffect() can be used to play dual-rumble vibration effects.
Multimedia 🎥
GStreamer-based multimedia support for WebKit, including (but not limited to) playback, capture, WebAudio, WebCodecs, and WebRTC.
VIDEO_DECODING_LIMIT is now configurable at runtime, in addition to build time. That will allow vendors that share a single binary build on different platforms to fine-tune their needs without a rebuild.
Graphics 🖼️
Landed a change that tweaks the text rendering done with Skia. With this change, the text looks more natural now - just like in other browsers. However, this is done by blending text incorrectly as a compromise.
Releases 📦️
One more set of release candidates for the upcoming stable branch,
WebKitGTK 2.51.93 and
WPE WebKit 2.51.93,
have been published. For those interested in previewing the upcoming 2.52.x
series this release is expected to be quite stable. Reporting issues in Bugzilla are,
as usual, more than welcome.
Update on what happened in WebKit in the week from February 23 to March 2.
This installment of the periodical brings news about support
for Qualcomm qtivdec2 and qtivenc2 on GStreamer, GPU texture
atlas creation and replay substitution, enhancement of the scroll
gesture in WPE, and two new releases: WebKitGTK 2.51.92 and WPE
WebKit 2.51.92.
Cross-Port 🐱
Multimedia 🎥
GStreamer-based multimedia support for WebKit, including (but not limited to) playback, capture, WebAudio, WebCodecs, and WebRTC.
Work on adding support for the Qualcomm GStreamer qtivdec2 and qtivenc2 elements is on-going
Graphics 🖼️
Implemented GPU texture atlas creation and replay substitution in the Skia painting engine on GTK/WPE. After recording, raster images are packed into GPU atlases via BitmapTexture, with two upload paths: an optimized DMA-buf path that memory-maps GPU buffers and dispatches uploading to a dedicated worker thread, and a synchronous GL fallback using BitmapTexture::updateContents(). Atlas uploads are synchronized across workers using a countdown-latch fence. During replay, SkiaReplayCanvas intercepts raster image draws and substitutes them with atlas texture draws, mapping source coordinates into atlas space.
WPE WebKit 📟
WPE Platform API 🧩
New, modern platform API that supersedes usage of libwpe and WPE backends.
The recent WPE WebKit 2.51.92 release is the first one to have its WPEPlatform documentation online, but it was not included in the tarball. This issue has been corrected and tarballs for future releases will also include this documentation.
Scrolling using touch input with WPEPlatform would result in scrolling faster when more than one touch point was in effect. The gesture detector has been fixed to make scrolling have always a consistent speed.
Releases 📦️
The third —and likely the last— release candidates for the upcoming stable branch, WebKitGTK 2.51.92 and WPE WebKit 2.51.92, have been published. For those interested in previewing the upcoming 2.52.x series this release is expected to be quite stable; but there might be still some rough edges. Reporting issues in Bugzilla are, as usual, more than welcome.
Update on what happened in WebKit in the week from February 9 to February 23.
In this week we have a nice fix for video streams timestamps, a fix
for a PDF rendering regression, support for rendering video buffers
provided by Qualcomm video decoders, and a fix for a font selection
issue. Also notable we had a new WPE Android release, and the libsoup
3.6.6 release.
Fixed a PDF rendering regression caused by the canvas 2D operation recording feature, where switching between the recording canvas and the GPU surface canvas failed to preserve the full save/restore nesting, clip stack, and transparency layer state. Replaced the fragile state-copying approach with a state replay mechanism in GraphicsContextSkia that tracks the full sequence of save restore, clip, and transparency layer operations, then reconstructs the exact nesting on the target canvas when flushing a recording.
Added support for rendering video buffers provided by Qualcomm hardware-accelerated decoders, with aid from the EXT_YUV_target OpenGL extension.
Fixed the font selection issue that the system fallback font cache mixed up different font styles.
Releases 📦️
WPE Android 0.3.2 has been released, and prebuilt packages are available at the Maven Central repository. This is a stable maintenance release which updates WPE WebKit to 2.50.5, which is the most recent stable release.
libsoup 3.6.6 has been released with numerous bug and security fixes.
Update on what happened in WebKit in the week from February 2 to February 9.
The main event this week was FOSDEM (pun intended), which included
presentations related to WebKit; but also we got a batch of stable
and development releases, asynchronous scrolling work, OpenGL
logging, cleanups, and improving the inspector for the WPE work.
Cross-Port 🐱
Graphics 🖼️
While asynchronous scrolling for mouse wheel events was already supported,
scrollbar layers were still being painted on the main thread. This has been
changed to paint scrollbars on the
scrolling thread instead, which avoids scrollbars to “lag” behind scrolled
content.
Fixed flickering caused by the
combination of damage tracking and asynchronous scrolling for mouse wheel
events.
Figuring out the exact location inside WebKit that triggered an OpenGL issue
may still be challenging with this aid, and therefore a backtrace will be
appended in case of errors to help
pinpoint the source, when the log channel is enabled at the “debug” level with
GLContext=debug.
Configuring the build with USE_SKIA=OFF to make WebKit use the
Cairo graphics library is no longer
supported. Using
Skia has been the default since late
2024,
and after two full years the 2.54.0 release (due in September 2026)
will be the first one where the choice is no longer possible.
WebKitGTK 🖥️
The “on demand” hardware acceleration policy has been rarely used lately, and
thus support for it has been removed.
Note that this affects only the GTK port when built with GTK 3—the option never
existed when using GTK 4.
Existing GTK 3 applications that use
WEBKIT_HARDWARE_ACCELERATION_POLICY_ON_DEMAND will continue to work and do
not need rebuilding: they will be promoted to use the “always enabled” policy
starting with WebKitGTK 2.54.0 (due in September 2026).
WPE WebKit 📟
The Web Inspector has received
support for saving data to local
files, allowing things such as saving page resources or exporting the network
session to a HAR archive.
Note that using the Web Inspector locally is supported when using the
WPEPlatform API, and the keyboard shortcut Ctrl+Shift+I may be used to bring it up.
Releases 📦️
WebKitGTK
2.50.5 and
WPE WebKit 2.50.5 have
been released. These are stable maintenance releases that improves stability,
correct bugs, and fixes small rendering issues.
The second release candidates for the upcoming stable branch, WebKitGTK
2.51.91 and
WPE WebKit 2.51.91,
have been published as well. Those using those to preview the upcoming 2.52.x
series are encouraged to provide bug reports in
Bugzilla for any issue they may experience.
Community & Events 🤝
We have published a blog
post
on our work implementing the
Temporal proposal in JavaScriptCore,
WebKit's JavaScript engine.
This year's edition of FOSDEM took place in
Brussels between January 31st and February 1st, and featured a number of
sessions related to WebKitGTK and WPE:
Update on what happened in WebKit in the week from January 26 to February 2.
A calm week for sure! The highlight this week is the fix for scrolling not starting when the main thread is blocked.
Cross-Port 🐱
Graphics 🖼️
Fixed the problem of wheel event async scrolling doesn't start while the main thread is blocked. This should make WebKit feel more responsive even on heavier websites.
Update on what happened in WebKit in the week from January 19 to January 26.
The main event this week has been the creation of the branch for the upcoming stable series, accompanied by the first release candidate before 2.52.0. But there's more: the WPE port gains hyphenation support and the ability to notify of graphics buffer changes; both ports get graphics fixes and a couple of new Web features, and WPE-Android also gets a new stable release.
Cross-Port 🐱
Implemented support for the :open
pseudo-class on dialog and details elements. This is currently behind the
OpenPseudoClass feature flag.
Implemented the source property for
ToggleEvent. This can be used to run code dependent on the triggering element
in response to a popover or dialog toggle.
Graphics 🖼️
Fixed the rendering glitches with
wheel event asynchronous scrolling, which occurred when the page was scrolled
to areas not covered by tiles while the main thread was blocked.
WPE WebKit 📟
Support for
hyphenation
has been added to WPE. This requires
libhyphen and can be disabled at build-time with the USE_LIBHYPHEN=OFF
CMake option.
WPE Platform API 🧩
New, modern platform API that supersedes usage of libwpe and WPE backends.
WPEPlatform gained support to notify
changes in the configuration of graphics buffers allocated to render the
contents of a web view, either by handling the WPEView::buffers-changed
signal or by overriding the WPEViewClass.buffers_changed virtual function.
This feature is mainly useful for platform implementations which may need to
perform additional setup in advance, before updated web view contents are
provided in the buffers configured by WebKit.
Releases 📦️
WPE-Android 0.3.0
has been released, and prebuilt packages are available at the Maven Central
repository.
The main change in this this version is the update to WPE WebKit 2.50.4, which
is the most recent stable release.
A new branch has been
created for the
upcoming 2.52.x stable release series of the GTK and WPE WebKit ports. The
first release candidates from this branch, WebKitGTK
2.51.90 and
WPE WebKit 2.51.90 are
now available. Testing and issue reports in Bugzilla
are welcome to help with stabilization before the first stable release, which
is planned for mid-March.
Now that 2025 is over, it’s time to look back and feel proud of the path we’ve walked. Last year has been really exciting in terms of contributions to GStreamer and WebKit for the Igalia Multimedia team.
With more than 459 contributions along the year, we’ve been one of the top contributors to the GStreamer project, in areas like Vulkan Video, GstValidate, VA, GStreamer Editing Services, WebRTC or H.266 support.
Igalia’s contributions to the GStreamer project
In Vulkan Video we’ve worked on the VP9 video decoder, and cooperated with other contributors to push the AV1 decoder as well. There’s now an H.264 base class for video encoding that is designed to support general hardware-accelerated processing.
GStreaming Editing Services, the framework to build video editing applications, has gained time remapping support, which now allows to include fast/slow motion effects in the videos. Video transformations (scaling, cropping, rounded corners, etc) are now hardware-accelerated thanks to the addition of new Skia-based GStreamer elements and integration with OpenGL. Buffer pool tuning and pipeline improvements have helped to optimize memory usage and performance, enabling the edition of 4K video at 60 frames per second. Much of this work to improve and ensure quality in GStreamer Editing Services has also brought improvements in the GstValidate testing framework, which will be useful for other parts of GStreamer.
Regarding H.266 (VVC), full playback support (with decoders such as vvdec and avdec_h266, demuxers and muxers for Matroska, MP4 and TS, and parsers for the vvc1 and vvi1 formats) is now available in GStreamer 1.26 thanks to Igalia’s work. This allows user applications such as the WebKitGTK web browser to leverage the hardware accelerated decoding provided by VAAPI to play H.266 video using GStreamer.
Igalia has also been one of the top contributors to GStreamer Rust, with 43 contributions. Most of the commits there have been related to Vulkan Video.
Igalia’s contributions to the GStreamer Rust project
In addition to GStreamer, the team also has a strong presence in WebKit, where we leverage our GStreamer knowledge to implement many features of the web engine related to multimedia. From the 1739 contributions to the WebKit project done last year by Igalia, the Multimedia team has made 323 of them. Nearly one third of those have been related to generic multimedia playback, and the rest have been on areas such as WebRTC, MediaStream, MSE, WebAudio, a new Quirks system to provide adaptations for specific hardware multimedia platforms at runtime, WebCodecs or MediaRecorder.
Igalia Multimedia Team’s contributions to different areas of the WebKit project
We’re happy about what we’ve achieved along the year and look forward to maintaining this success and bringing even more exciting features and contributions in 2026.
Update on what happened in WebKit in the week from December 26 to January 19.
We're back! The first periodical of 2026 brings you performance optimizations, improvements to the memory footprint calculation, new APIs, the removal of the legacy Qt5 WPE backend, and as always, progress on JSC's Temporal implementation.
Cross-Port 🐱
The memory footprint calculation mechanism has been unified across GTK, JSC, and WPE ports. Therefore, the expensive /proc/self/smaps is not used anymore and the WPE uses /proc/self/statm with extra cache now to prevent frequent file reading.
Added a new webkit_context_menu_get_position() function to the API that allows obtaining the pointer coordinates, relative to the web view origin, at the moment when a context menu was triggered.
Additionally, behaviour of context menus has been made more consistent between the GTK and WPE ports, and handling of GAction objects attached to menu items has been rewritten and improved with the goal of better supporting context menus in the WPE port.
JavaScriptCore 🐟
The built-in JavaScript/ECMAScript engine for WebKit, also known as JSC or SquirrelFish.
In JavaScriptCore's implementation of Temporal, fixed a bug in Temporal.PlainTime.from that read options in the wrong order, which caused a test262 test to fail.
In JavaScriptCore's implementation of Temporal, fixed several bugs in PlainYearMonth methods and enabled all PlainYearMonth tests that don't depend on the Intl object. This completes the implementation of Temporal PlainYearMonth objects in JSC.
Graphics 🖼️
In WebKit's Skia graphics backend, fixed GrDirectContext management for GPU resources. Operations on GPU-backed resources must use the context that created them, not the current thread's context. The fix stores GrDirectContext at creation time for NativeImage and uses surface->recordingContext()->asDirectContext() for SkSurface, correcting multiple call sites that previously used the shared display's context incorrectly.
Damage propagation has been added to the recently-added, non-composited mode in WPE.
In WebKit's Skia graphics backend for GTK/WPE, added canvas 2D operation recording for GPU-accelerated rendering. Instead of executing drawing commands immediately, operations are recorded into an SkPicture and replayed in batch when the canvas contents are needed, reducing GPU state change overhead for workloads with many small drawing operations, improving the MotionMark Canvas Lines performance on embedded devices with low-end tiled GPUs.
WPE WebKit 📟
Due to Qt5 not receiving maintenance since mid-2025, the WPE Qt5 binding that used the legacy libwpe API has been removed from the tree. The Qt6 binding remains part of the source tree, which is a better alternative that allows using supported Qt versions, and is built atop the new WPEPlatform API, making it a future-proof option. The WPE Qt API may be enabled when configuring the build with CMake, using the ENABLE_WPE_QT_API option.
WPE Platform API 🧩
New, modern platform API that supersedes usage of libwpe and WPE backends.
The WPEScreenSyncObserver class has been improved to support multiple callbacks. Instead of a single callback set with wpe_screen_sync_observer_set_callback(), clients of the API can now use wpe_screen_sync_observer_add_callback() and wpe_screen_sync_observer_remove_callback(). The observer will be paused automatically when there are no callbacks attached to it.
Update on what happened in WebKit in the week from December 16 to December 25.
Right during the holiday season 🎄, the last WIP installment of the year comes packed with new releases, a couple of functions added to the public API, cleanups, better timer handling, and improvements to MathML and WebXR support.
Cross-Port 🐱
Landed support for font-size: math. Now
math-depth
can automatically control the font size inside of <math> blocks, making
scripts and nested content smaller to improve readability and presentation.
webkit_context_menu_item_get_gaction_target() to obtain the GVariant
associated with a context menu item created from a GAction.
webkit_context_menu_item_get_title() may be used to obtain
the title of a context menu item.
Improved timers, by making some of
them use the timerfd API. This reduces
timer “lateness”—the amount of time elapsed between the configured trigger
time, and the effective one—, which in turn improves the perceived smoothness
of animations thanks to steadier frame delivery timings. Systems where the
timerfd_create and timerfd_settime functions are not available will
continue working as before.
On the WebXR front, support was added
for XR_TRACKABLE_TYPE_DEPTH_ANDROID through the XR_ANDROID_trackables
extension, which allows reporting depth information for elements that take part
in hit testing.
Graphics 🖼️
Landed a change that implements
non-composited page rendering in the WPE port. This new mode is disabled by
default, and may be activated by disabling the AcceleratedCompositing runtime
preference. In such case, the frames are rendered using a simplified code path
that does not involve the internal WebKit compositor. Therefore it may offer a
better performance in some specific cases on constrained embedded devices.
Since version 2.10.2, the FreeType library can be built
with direct support for loading fonts in the
WOFF2 format. Until now, the WPE and GTK WebKit
ports used libwoff2 in an intermediate step
to convert those fonts on-the-fly before handing them to FreeType for
rendering. The CMake build system will now detect when FreeType supports WOFF2
directly and skip the conversion step.
This way, in systems which provide a suitable version of FreeType, libwoff2
will no longer be needed.
WPE WebKit 📟
WPE Platform API 🧩
New, modern platform API that supersedes usage of libwpe and WPE backends.
The legacy libwpe-based API can now be disabled at build
time, by toggling the
ENABLE_WPE_LEGACY_API CMake option. This allows removal of uneeded code when
an application is exclusively using the new WPEPlatform API.
Adaptation of WPE WebKit targeting the Android operating system.
AHardwareBuffer
is now supported as backing for
accelerated graphics surfaces that can be shared across processes. This is the
last piece of the puzzle to use WPEPlatform on Android without involving
expensive operations to copy rendered frames back-and-forth between GPU and
system memory.
Releases 📦️
WebKitGTK
2.50.4 and
WPE WebKit 2.50.4 have
been released. These stable releases include a number of important patches for
security issues, and we urge users and distributors to update to this release
if they have not yet done it. An accompanying security advisory,
WSA-2025-0010, has been published
(GTK,
WPE).
This article is a continuation of the series on WPE performance considerations. While the previous article touched upon fairly low-level aspects of the DOM tree overhead,
this one focuses on more high-level problems related to managing the application’s workload over time. Similarly to before, the considerations and conclusions made in this blog post are strongly related to web applications
in the context of embedded devices, and hence the techniques presented should be used with extra care (and benchmarking) if one would like to apply those on desktop-class devices.
Typical web applications on embedded devices have their workloads distributed over time in various ways. In practice, however, the workload distributions can usually be fitted into one of the following categories:
Idle applications with occasional updates - the applications that present static content and are updated at very low intervals. As an example, one can think of some static dashboard that presents static content and switches
the page every, say, 60 seconds - such as e.g. a static departures/arrivals dashboard on the airport.
Idle applications with frequent updates - the applications that present static content yet are updated frequently (or are presenting some dynamic content, such as animations occasionally). In that case, one can imagine a similar
airport departures/arrivals dashboard, yet with the animated page scrolling happening quite frequently.
Active applications with occasional updates - the applications that present some dynamic content (animations, multimedia, etc.), yet with major updates happening very rarely. An example one can think of in this case is an application
playing video along with presenting some metadata about it, and switching between other videos every few minutes.
Active applications with frequent updates - the applications that present some dynamic content and change the surroundings quite often. In this case, one can think of a stock market dashboard continuously animating the charts
and updating the presented real-time statistics very frequently.
Such workloads can be well demonstrated on charts plotting the browser’s CPU usage over time:
As long as the peak workload (due to updates) is small, no negative effects are perceived by the end user. However, when the peak workload is significant, some negative effects may start getting noticeable.
In case of applications from groups (1) and (2) mentioned above, a significant peak workload may not be a problem at all. As long as there are no continuous visual changes and no interaction is allowed during updates, the end-user
is unable to notice that the browser was not responsive or missed some frames for some period of time. In such cases, the application designer does not need to worry much about the workload.
In other cases, especially the ones involving applications from groups (3) and (4) mentioned above, the significant peak workload may lead to visual stuttering, as any processing making the browser busy for longer than 16.6 milliseconds
will lead to lost frames. In such cases, the workload has to be managed in a way that the peaks are reduced either by optimizing them or distributing them over time.
The first step to addressing the peak workload is usually optimization. Modern web platform gives a full variety of tools to optimize all the stages of web application processing done by the browser. The usual process of optimization is a
2-step cycle starting with measuring the bottlenecks and followed by fixing them. In the process, the usual improvements involve:
using CSS containment,
using shadow DOM,
promoting certain parts of the DOM to layers and manipulating them with transforms,
parallelizing the work with workers/worklets,
using the visibility CSS property to separate painting from layout,
optimizing the application itself (JavaScript code, the structure of the DOM, the architecture of the application),
Unfortunately, in practice, it’s not uncommon that even very well optimized applications still have too much of a peak workload for the constrained embedded devices they’re used on. In such cases, the last resort solution is
pre-rendering. As long as it’s possible from the application business-logic perspective, having at least some web page content pre-rendered is very helpful in situations when workload has to be managed, as pre-rendering
allows the web application designer to choose the precise moment when the content should actually be rendered and how it should be done. With that, it’s possible to establish a proper trade-off between reduction in peak workload and
the amount of extra memory used for storing the pre-rendered contents.
Nowadays, the web platform provides at lest a few widely-adapted APIs that provide means for the application to perform various kinds of pre-rendering. Also, due to the ways the browsers are implemented, some APIs can be purposely misused
to provide pre-rendering techniques not necessarily supported by the specification. However, in the pursuit of good trade-offs, all the possibilities should be taken into account.
Before jumping into particular pre-rendering techniques, it’s necessary to emphasize that the pre-rendering term used in this article refers to the actual rendering being done earlier than it’s visually presented. In that
sense, the resource is rasterized to some intermediate form when desired and then just composited by the browser engine’s compositor later.
The most basic (and limited at the same time) pre-rendering technique is one that involves rendering offline i.e. before the browser even starts. In that case, the first limitation is that the content to be rendered must be known
beforehand. If that’s the case, the rendering can be done in any way, and the result may be captured as e.g. raster or vector image (depending on the desired trade-off). However, the other problem is that such a rendering is usually out of
the given web application scope and thus requires extra effort. Moreover, depending on the situation, the amount of extra memory used, the longer web application startup (due to loading the pre-rendered resources), and the processing
power required to composite a given resource, it may not always be trivial to obtain the desired gains.
The first group of actual pre-rendering techniques happening during web application runtime is related to Canvas and
OffscreenCavas. Those APIs are really useful as they offer great flexibility in terms of usage and are usually very performant.
However, in this case, the natural downside is the lack of support for rendering the DOM inside the canvas. Moreover, canvas has a very limited support for painting text — unlike the DOM, where
CSS has a significant amount of features related to it. Interestingly, there’s an ongoing proposal called HTML-in-Canvas that could resolve those limitations
to some degree. In fact, Blink has a functioning prototype of it already. However, it may take a while before the spec is mature and widely adopted by other browser engines.
When it comes to actual usage of canvas APIs for pre-rendering, the possibilities are numerous, and there are even more of them when combined with processing using workers.
The most popular ones are as follows:
rendering to an invisible canvas and showing it later,
rendering to a canvas detached from the DOM and attaching it later,
rendering to an invisible/detached canvas and producing an image out of it to be shown later,
rendering to an offscreen canvas and producing an image out of it to be shown later.
When combined with workers, some of the above techniques may be used in the worker threads with the rendered artifacts transferred to the main for presentation purposes. In that case, one must be careful with
the transfer itself, as some objects may get serialized, which is very costly. To avoid that, it’s recommended to use transferable objects
and always perform a proper benchmarking to make sure the transfer is not involving serialization in the particular case.
While the use of canvas APIs is usually very straightforward, one must be aware of two extra caveats.
First of all, in the case of many techniques mentioned above, there is no guarantee that the browser will perform actual rasterization at the given point in time. To ensure the rasterization is triggered, it’s usually
necessary to enforce it using e.g. a dummy readback (getImageData()).
Finally, one should be aware that the usage of canvas comes with some overhead. Therefore, creating many canvases or creating them often, may lead to performance problems that could outweigh the gains from the
pre-rendering itself.
The second group of pre-rendering techniques happening during web application runtime is limited to the DOM rendering and comes out of a combination of purposeful spec misuse and tricking the browser engine into making it rasterizing
on demand. As one can imagine, this group of techniques is very much browser-engine-specific. Therefore, it should always be backed by proper benchmarking of all the use cases on the target browsers and target hardware.
In principle, all the techniques of this kind consist of 3 parts:
Enforcing the content to be pre-rendered being placed on a separate layer backed by an actual buffer internally in the browser,
Tricking the browser’s compositor into thinking that the layer needs to be rasterized right away,
Ensuring the layer won’t be composited eventually.
When all the elements are combined together, the browser engine will allocate an internal buffer (e.g. texture) to back the given DOM fragment, it will process that fragment (style recalc, layout), and rasterize it right away. It will do so
as it will not have enough information to allow delaying the rasterization of the layer (as e.g. in case of display: none). Then, when the compositing time comes, the layer will turn out to be invisible in practice
due to e.g. being occluded, clipped, etc. This way, the rasterization will happen right away, but the results will remain invisible until a later time when the layer is made visible.
In practice, the following approaches can be used to trigger the above behavior:
for (1), the CSS properties such as will-change: transform, z-index, position: fixed, overflow: hidden etc. can be used depending on the browser engine,
for (2) and (3), the CSS properties such as opacity: 0, overflow: hidden, contain: strict etc. can be utilized, again, depending on the browser engine.
The scrolling trick
While the above CSS properties allow for various combinations, in case of WPE WebKit in the context of embedded devices (tested on NXP i.MX8M Plus), the combination that has proven to yield the best performance benefits turns
out to be a simple approach involving overflow: hidden and scrolling. The example of such an approach is explained below.
With the number of idle frames (if) set to 59, the idea is that the application does nothing significant for the 59 frames, and then every 60th frame it updates all the numbers in the table.
As one can imagine, on constrained embedded devices, such an approach leads to a very heavy workload during every 60th frame and hence to lost frames and unstable application’s FPS.
As long as the numbers are available earlier than every 60th frame, the above application is a perfect example where pre-rendering could be used to reduce the peak workload.
To simulate that, the 3 variants of the approach involving the scrolling trick were prepared for comparison with the above:
In the above demos, the idea is that each cell with a number becomes a scrollable container with 2 numbers actually — one above the other. In that case, because overflow: hidden is set, only one of the numbers is visible while the
other is hidden — depending on the current scrolling:
With such a setup, it’s possible to update the invisible numbers during idle frames without the user noticing. Due to how WPE WebKit accelerates the scrolling, changing the invisible
numbers, in practice, triggers the layout and rendering right away. Moreover, the actual rasterization to the buffer backing the scrollable container happens immediately (depending on the tiling settings), and hence the high cost of layout
and text rasterization can be distributed. When the time comes, and all the numbers need to be updated, the scrollable containers can be just scrolled, which in that case turns out to be ~2 times faster than updating all the numbers in place.
While the first sysprof trace shows very little processing during 11 idle frames and a big chunk of processing (21 ms) every 12th frame, the second sysprof trace shows how the distribution of load looks. In
that case, the amount of work during 11 idle frames is much bigger (yet manageable), but at the same time, the formerly big chunk of processing every 12th frame is reduced almost 2 times (to 11 ms). Therefore, the overall
frame rate in the application is much better.
Results
Despite the above improvement speaking for itself, it’s worth summarizing the improvement with the benchmarking results of the above demos obtained from the NXP i.MX8M Plus and presenting the application’s average
frames per second (FPS):
Clearly, the positive impact of pre-rendering can be substantial depending on the conditions. In practice, when the rendered DOM fragment is more complex, the trick such as above can yield even better results.
However, due to how tiling works, the effect can be minimized if the content to be pre-rendered spans multiple tiles. In that case, the browser may defer rasterization until the tiles are actually needed. Therefore,
the above needs to be used with care and always with proper benchmarking.
As demonstrated in the above sections, when it comes to pre-rendering the contents to distribute the web application workload over time, the web platform gives both the official APIs to do it, as well as unofficial
means through purposeful misuse of APIs and exploitation of browser engine implementations. While this article hasn’t covered all the possibilities available, the above should serve as a good initial read with some easy-to-try
solutions that may yield surprisingly good results. However, as some of the ideas mentioned above are very much browser-engine-specific, they should be used with extra care and with the limitations (lack of portability)
in mind.
As the web platform constantly evolves, the pool of pre-rendering techniques and tricks should keep evolving as well. Also, as more and more web applications are used on embedded devices, more pressure should be
put on the specification, which should yield more APIs targeting the low-end devices in the future. With that in mind, it’s recommended for the readers to stay up-to-date with the latest specification and
perhaps even to get involved if some interesting use cases would be worth introducing new APIs.
Update on what happened in WebKit in the week from December 8 to December 15.
In this end-of-year special have a new GMallocString helper that makes
management of malloc-based strings more efficient, development releases,
and a handful of advancements on JSC's implementation of Temporal, in
particular the PlainYearMonth class.
Cross-Port 🐱
Added GMallocString class to WTF to adopt UTF8 C strings and make them WebKit first class citizens efficiently (no copies). Applied in GStreamer code together with other improvements by using CStringView. Fixed other twobugs about string management.
JavaScriptCore 🐟
The built-in JavaScript/ECMAScript engine for WebKit, also known as JSC or SquirrelFish.
Development releases of WebKitGTK 2.51.3
and WPE WebKit 2.51.3
are now available. These include a number of API additions and new features,
and are intended to allow interested parties to test those in advance, prior
to the next stable release series. As usual, bug reports are
welcome in Bugzilla.
Update on what happened in WebKit in the week from December 1 to December 8.
In this edition of the periodical we have further advancements on
the Temporal implementation, support for Vivante super-tiled format,
and an adaptation of the DMA-BUF formats code to the Android port.
Cross-Port 🐱
JavaScriptCore 🐟
The built-in JavaScript/ECMAScript engine for WebKit, also known as JSC or SquirrelFish.
Implemented the toString, toJSON, and toLocaleString methods for PlainYearMonth objects in JavaScriptCore's implementation of Temporal.
Graphics 🖼️
BitmapTexture and TextureMapperwere prepared to handle textures where the logical size (e.g. 100×100) differs from the allocated size (e.g. 128×128) due to alignment requirements. This allowed to add support for using memory-mapped GPU buffers in the Vivante super-tiled format available on i.MX platforms. Set WEBKIT_SKIA_USE_VIVANTE_SUPER_TILED_TILE_TEXTURES=1 to activate at runtime.
WPE WebKit 📟
WPE Platform API 🧩
New, modern platform API that supersedes usage of libwpe and WPE backends.
The WPEBufferDMABufFormats class has been renamed to WPEBufferFormats, as it can be used in situations where mechanisms other than DMA-BUF may be used for buffer sharing—on Android targets AHardwareBuffer is used instead, for example. The naming change involved also WPEBufferFormatsBuilder (renamed from WPEBufferDMABufFormatsBuilder), and methods and signals in other classes that use these types. Other than the renames, there is no change in functionality.
Some years ago I had mentioned some command line tools I used to analyze and find useful information on GStreamer logs. I’ve been using them consistently along all these years, but some weeks ago I thought about unifying them in a single tool that could provide more flexibility in the mid term, and also as an excuse to unrust my Rust knowledge a bit. That’s how I wrote Meow, a tool to make cat speak (that is, to provide meaningful information).
The idea is that you can cat a file through meow and apply the filters, like this:
which means “select those lines that contain appsinknewsample (with case insensitive matching), but don’t contain V0 nor video (that is, by exclusion, only that contain audio, probably because we’ve analyzed both and realized that we should focus on audio for our specific problem), highlight the different thread ids, only show those lines with timestamp lower than 21.46 sec, and change strings like Source/WebCore/platform/graphics/gstreamer/mse/AppendPipeline.cpp to become just AppendPipeline.cpp“, to get an output as shown in this terminal screenshot:
Cool, isn’t it? After all, I’m convinced that the answer to any GStreamer bug is always hidden in the logs (or will be, as soon as I add “just a couple of log lines more, bro” ).
Currently, meow supports this set of manipulation commands:
Word filter and highlighting by regular expression (fc:REGEX, or just REGEX): Every expression will highlight its matched words in a different color.
Filtering without highlighting (fn:REGEX): Same as fc:, but without highlighting the matched string. This is useful for those times when you want to match lines that have two expressions (E1, E2) but the highlighting would pollute the line too much. In those case you can use a regex such as E1.*E2 and then highlight the subexpressions manually later with an h: rule.
Negative filter (n:REGEX): Selects only the lines that don’t match the regex filter. No highlighting.
Highlight with no filter (h:REGEX): Doesn’t discard any line, just highlights the specified regex.
Substitution (s:/REGEX/REPLACE): Replaces one pattern for another. Any other delimiter character can be used instead of /, it that’s more convenient to the user (for instance, using # when dealing with expressions to manipulate paths).
Time filter (ft:TIME-TIME): Assuming the lines start with a GStreamer log timestamp, this filter selects only the lines between the target start and end time. Any of the time arguments (or both) can be omitted, but the - delimiter must be present. Specifying multiple time filters will generate matches that fit on any of the time ranges, but overlapping ranges can trigger undefined behaviour.
Highlight threads (ht:): Assuming a GStreamer log, where the thread id appears as the third word in the line, highlights each thread in a different color.
The REGEX pattern is a regular expression. All the matches are case insensitive. When used for substitutions, capture groups can be defined as (?CAPTURE_NAMEREGEX).
The REPLACEment string is the text that the REGEX will be replaced by when doing substitutions. Text captured by a named capture group can be referred to by ${CAPTURE_NAME}.
The TIME pattern can be any sequence of numbers, : or . . Typically, it will be a GStreamer timestamp (eg: 0:01:10.881123150), but it can actually be any other numerical sequence. Times are compared lexicographically, so it’s important that all of them have the same string length.
The filtering algorithm has a custom set of priorities for operations, so that they get executed in an intuitive order. For instance, a sequence of filter matching expressions (fc:, fn:) will have the same priority (that is, any of them will let a text line pass if it matches, not forbidding any of the lines already allowed by sibling expressions), while a negative filter will only be applied on the results left by the sequence of filters before it. Substitutions will be applied at their specific position (not before or after), and will therefore modify the line in a way that can alter the matching of subsequent filters. In general, the user doesn’t have to worry about any of this, because the rules are designed to generate the result that you would expect.
Now some practical examples:
Example 1: Select lines with the word “one”, or the word “orange”, or a number, highlighting each pattern in a different color except the number, which will have no color:
$ cat file.txt | meow one fc:orange 'fn:[0-9][0-9]*' 000 one small orange 005 one big orange
Example 2: Assuming a pictures filename listing, select filenames not ending in “jpg” nor in “jpeg”, and rename the filename to “.bak”, preserving the extension at the end:
Example 3: Only print the log lines with times between 0:00:24.787450146 and 0:00:24.790741865 or those at 0:00:30.492576587 or after, and highlight every thread in a different color:
This is only the begining. I have great ideas for this new tool (as time allows), such as support for parenthesis (so the expressions can be grouped), or call stack indentation on logs generated by tracers, in a similar way to what Alicia’s gst-log-indent-tracers tool does. I might also predefine some common expressions to use in regular expressions, such as the ones to match paths (so that the user doesn’t have to think about them and reinvent the wheel every time). Anyway, these are only ideas. Only time and hyperfocus slots will tell…
Update on what happened in WebKit in the week from November 24 to December 1.
The main highlights for this week are the completion of `PlainMonthDay`
in Temporal, moving networking access for GstWebRTC to the WebProcess,
and Xbox Cloud Gaming now working in the GTK and WPE ports.
Cross-Port 🐱
Multimedia 🎥
GStreamer-based multimedia support for WebKit, including (but not limited to)
playback, capture, WebAudio, WebCodecs, and WebRTC.
Support for remote inbound RTP statistics was improved in
303671@main, we now properly report
framesPerSecond and totalDecodeTime metrics, those fields are used in the
Xbox Cloud Gaming service to show live stats about the connection and video
decoder performance in an overlay.
The GstWebRTC backend now relies on
librice for its
ICE.
The Sans-IO architecture of librice allows us to keep the WebProcess sandboxed
and to route WebRTC-related UDP and (eventually) TCP packets using the
NetworkProcess. This work landed in
303623@main. The GNOME SDK should
also soon ship
librice.
Support for seeking in looping videos was fixed in
303539@main.
JavaScriptCore 🐟
The built-in JavaScript/ECMAScript engine for WebKit, also known as JSC or SquirrelFish.
Implemented the valueOf and
toPlainDate for PlainMonthDay objects.
This completes the implementation of
TemporalPlainMonthDay objects in JSC!
WebKitGTK 🖥️
The GTK port has gained support for
interpreting touch input as pointer
events. This
matches the behaviour of other browsers by following the corresponding
specifications.
WPE WebKit 📟
Fixed an issue that prevented
WPE from processing further input events after receiving a secondary mouse
button press.
Fixed an issue that caused right
mouse button clicks to prevent processing of further pointer events.
WPE Platform API 🧩
New, modern platform API that supersedes usage of libwpe and WPE backends.
We landed a patch to add a new signal
in WPEDisplay to notify when the connection to the native display has been lost.
Note that this work removed the support for building against
Enchant 1.x, and only version 2 will be
supported. The first stable release to require Enchant 2.x will be 2.52.0 due
in March 2026. Major Linux and BSD distributions have included Enchant 2
packages for years, and therefore this change is not expected to cause any
trouble. The Enchant library is used by the GTK port for spell checking.
Community & Events 🤝
We have published an
article detailing our
work making MathML
interoperable across browser engines! It has live demonstrations and feature
tables with our progress on WebKit support.
We have published new blogs post highlighting the most important changes in
both WPE WebKit
and WebKitGTK 2.50.
Enjoy!
This fall, the WPE WebKit team has released the 2.50 series of the Web engine after six months of hard work. Let’s have a deeper look at some of the most interesting changes in this release series!
Improved rendering performance
For this series, the threaded rendering implementation has been switched to use the Skia API. What has changed is the way we record the painting commands for each layer. Previously we used WebCore’s built-in mechanism (DisplayList) which is not thread-safe, and led to obscure rendering issues in release builds and/or sporadic assertions in debug builds when replaying the display lists in threads other than the main one. The DisplayList usage was replaced with SkPictureRecorder, Skia’s built-in facility, that provides similar functionality but in a thread-safe manner. Using the Skia API, we can leverage multithreading in a reliable way to replay recorded drawing commands in different worker threads, improving rendering performance.
An experimental hybrid rendering mode has also been added. In this mode, WPE WebKit will attempt to use GPU worker threads for rendering but, if these are busy, CPU worker threads will be used whenever possible. This rendering mode is still under investigation, as it is unclear whether the improvements are substantial enough to justify the extra complexity.
Damage propagation to the system compositor, which was added during the 2.48 cycle but remained disabled by default, has now been enabled. The system compositor may now leverage the damage information for further optimization.
Vertical writing-mode rendering has also received improvements for this release series.
Changes in Multimedia support
When available in the system, WebKit can now leverage the XDG desktop portal for accessing capture devices (like cameras) so that no specific sandbox exception is required. This provides secure access to capture devices in browser applications that use WPE WebKit.
Managed Media Source support has been enabled. This potentially improves multimedia playback, for example in mobile devices, by allowing the user agent to react to changes in memory and CPU availability.
Transcoding is now using the GStreamer built-in uritranscodebin element instead of GstTranscoder, which improves stability of the media recording that needs transcoding.
SVT-AV1 encoder support has been added to the media backend.
WebXR support
The WebXR implementation had been stagnating since it was first introduced, and had a number of shortcomings. This was removed in favor of a new implementation, also built using OpenXR, that better adapts to the multiprocess architecture of WebKit.
This feature is considered experimental in 2.50, and while it is complete enough to load and display a number of immersive experiences, a number of improvements and optional features continue to be actively developed. Therefore, WebXR support needs to be enabled at build time with the ENABLE_WEBXR=ON CMake option.
Android support
Support for Android targets has been greatly improved. It is now possible to build WPE WebKit without the need for additional patches when using the libwpe-based WPEBackend-android. This was achieved by incorporating changes that make WebKit use more appropriate defaults (like disabling MediaSession) or using platform-specific features (like ASharedMemory and AHardwareBuffer) when targeting Android.
The WebKit logging system has gained support to use the Android logd service. This is particularly useful for both WebKit and application developers, allowing to configure logging channels at runtime in any WPE WebKit build. For example, the following commands may be used before launching an application to debug WebGL setup and multimedia playback errors:
There is an ongoing effort to enable the WPEPlatform API on Android, and while it builds now, rendering is not yet working.
Web Platform support
As usual, changes in this area are extensive as WebKit constantly adopts, improves, and supports new Web Platform features. However, some interesting additions in this release cycle include:
Work continues on the new WPEPlatform API, which is still shipped as a preview feature in the 2.50 and needs to be explicitly enabled at build time with the ENABLE_WPE_PLATFORM=ON CMake option. The API may still change and applications developed using WPEPlatform are likely to need changes with future WPE WebKit releases; but not for long: the current goal is to have it ready and enabled by default for the upcoming 2.52 series.
One of the main changes is that WPEPlatform now gets built into libWPEWebKit. The rationale for this change is avoiding shipping two copies of shared code from the Web Template Framework (WTF), which saves both disk and memory space usage. The wpe-platform-2.0 pkg-config module is still shipped, which allows application developers to know whether WPEPlatform support has been built into WPE WebKit.
The abstract base class WPEScreenSyncObserver has been introduced, and allows platform implementations to notify on display synchronization, allowing WebKit to better pace rendering.
WPEPlatform has gained support for controllers like gamepads and joysticks through the new WPEGamepadManager and WPEGamepad classes. When building with the ENABLE_MANETTE=ON CMake option a built-in implementation based on libmanette is used by default, if a custom one is not specified.
WPEPlatform now includes a new WPEBufferAndroid class, used to represent graphics buffers backed by AHardwareBuffer. These buffers support being imported into an EGLImage using wpe_buffer_import_to_egl_image().
As part of the work to improve Android support, the buffer rendering and release fences have been moved from WPEBufferDMABuf to the base class, WPEBuffer. This is leveraged by WPEBufferAndroid, and should be helpful if more buffer types are introduced in the future.
Other additions include clipboard support, Interaction Media Features, and an accessibility implementation using ATK.
What’s new for WebKit developers?
WebKit now supports sending tracing marks and counters to Sysprof. Marks indicate when certain events occur and their duration; while counters track variables over time. Together, these allow developers to find performance bottlenecks and monitor internal WebKit performance metrics like frame rates, memory usage, and more. This integration enables developers to analyze the performance of applications, including data for WebKit alongside system-level metrics, in a unified view. For more details see this article, which also details how Sysprof was improved to handle the massive amounts of data produced by WebKit.
Finally, GCC 12.2 is now the minimum required version to build WPE WebKit. Increasing the minimum compiler version allows us to remove obsolete code and focus on improving code quality, while taking advantage of new C++ and compiler features.
Looking forward to 2.52
The 2.52 release series will bring even more improvements, and we expect it to be released during the spring of 2026. Until then!
Update on what happened in WebKit in the week from November 17 to November 24.
In this week's rendition, the WebView snapshot API was enabled on the WPE
port, further progress on the Temporal and Trusted Types implementations,
and the release of WebKitGTK and WPE WebKit 2.50.2.
Cross-Port 🐱
A WebKitImage-based implementation of WebView snapshot landed this week, enabling this feature on WPE when it was previously only available in GTK. This means you can now use webkit_web_view_get_snapshot (and webkit_web_view_get_snapshot_finish) to get a WebKitImage-representation of your screenshot.
WebKitImage implements the GLoadableIcon interface (as well as GIcon's), so you can get a PNG-encoded image using g_loadable_icon_load.
Remove incorrect early return in Trusted Types DOM attribute handling to align with spec changes.
JavaScriptCore 🐟
The built-in JavaScript/ECMAScript engine for WebKit, also known as JSC or SquirrelFish.
In JavaScriptCore's implementation of Temporal, implemented the with method for PlainMonthDay objects.
In JavaScriptCore's implementation of Temporal, implemented the from and equals methods for PlainMonthDay objects.
These stable releases include a number of patches for security issues, and as such a new security advisory, WSA-2025-0008, has been issued (GTK, WPE).
It is recommend to apply an additional patch that fixes building with the JavaScriptCore “CLoop” interpreter is enabled, which is typicall for architectures where JIT compilation is unsupported. Releases after 2.50.2 will include it and manual patching will no longer be needed.
Update on what happened in WebKit in the week from November 10 to November 17.
This week's update is composed of a new CStringView internal API, more
MathML progress with the implementation of the "scriptlevel" attribute,
the removal of the Flatpak-based SDK, and the maintanance update of
WPEBackend-fdo.
Cross-Port 🐱
Implement the MathML scriptlevel attribute using math-depth.
Finished implementing CStringView, which is a wrapper around UTF8 C strings. It allows you to recover the string without making any copies and perform string operations safely by taking into account the encoding at compile time.
Releases 📦️
WPEBackend-fdo 1.16.1 has been released. This is a maintenance update which adds compatibility with newer Mesa versions.
Infrastructure 🏗️
Most of the Flatpak-based SDK was removed. Developers are warmly encouraged to use the new SDK for their contributions to the Linux ports, this SDK has been successfully deployed on EWS and post-commits bots.