Project Fugu 🐡 at W3C TPAC

This week, I attended my now third W3C TPAC. After TPAC 2017 in Burlingame, CA, United States of America and TPAC 2018 in Lyon, France, TPAC 2019 was held in Fukuoka, Japan. For the first time, I felt like I could somewhat meaningfully contribute and had at least a baseline understanding of the underlying W3C mechanics. As each year, the TPAC agenda was crammed and overlaps were unavoidable. Below is the write-up of the meetings I had time to attend.

Day 1

On Monday, I attended the Service Workers Working Group (WG) meeting. The agenda this time was a mix of implementor updates, new proposals, and a lot of discussion of special cases. I know Jake Archibald is working on a summary post, so I leave it to him to summarize the day. The raw meeting minutes are available in case you're interested.

Day 2

On Tuesday, I visited the Web Application Security Working Group meeting as an observer. I was mostly interested in this WG because the agenda promised interesting proposals like Apple's /.well-known/change-password that was met with universal agreement. Some interesting discussion also sparked around again Apple's isLoggedIn() API proposal. I was reminded of why on the web we can't have nice things through an attack vector that leverages HSTS for tracking purposes. Luckily there is browser mitigation in place to prevent this. The meeting minutes cover the entire day.

Day 3

Wednesday was unconference day with 59(!) breakout sessions. Other than the at times tedious working group sessions, I find breakout sessions to be oftentimes more interesting and an opportunity to learn new things.

Breakout Session JS Built-in Modules

The first breakout session I attended was on JS built-in modules, a TC39 proposal by Apple for a JavaScript Built-in Library. The session's minutes are available, in general there was a lot of discussion and disagreement around namespaces and how built-in modules should be governed.

Breakout Session New Module Types: JSON, CSS, and HTML

The next session was on new module types for JSON, CSS, and HTML. As the developer of <dark-mode-toggle>, I'm fully in favor of getting rid of the clumsy innerHTML all the things!!!1! approach that vanilla JS custom elements currently make the programmer to follow. If you're likewise interested, subscribe to the CSS Modules issue and the HTML Modules issue in the Web Components WG repo. The discussion circulated mostly around details how @imports would work and how to convey the type of the import to avoid security issues, for example following the <link rel="preload"> way. The meeting minutes have the full details.

// Non-working example
import styles from 'styles.css' as stylesheet;
import settings from 'settings.json' as json;
Breakout Session Mini App Standardization

The Mini App Standardization session, organized by the Chinese Web Interest Group, was super interesting to me. In preparation of the Google Developer Days in Shanghai, China, that I spoke at right before TPAC, I have looked at WeChat mini programs and documented the developer experience and how close to and yet how far from the web they are. A couple of days before TPAC, the Chinese Web Interest Group had released a white paper that documents their ideas. The success the various mini apps platforms have achieved deserves our full respect. There were, however, various voices—including from the TAG—that urged the various stakeholders to converge their work with efforts made in the area of Progressive Web Apps, for example around the Web App Manifest rather than create yet another manifest-like format. Read the full session minutes for all details. One of the results of the session was the creation of the MiniApps Ecosystem Community Group that I hope to join.

Breakout Session For a More Capable Web—Project Fugu

Together with Anssi Kostiainen from Intel and John Jansen from Microsoft, I organized a breakout session for a more capable web under the umbrella of Project Fugu 🐡. You can see our slides embedded below. In the session we argue that to remain relevant with native, hybrid, or mini apps, web apps, too, need access to a comparable set of APIs. We briefly touched upon the APIs being worked on by the cross-company project partners, and then opened the floor for an open discussion on why we see the browser-accessible web in danger if we don't move it forward now, despite all fully acknowledged challenges around privacy, security, and compatibility. You can follow the discussion in the excellent(!) session minutes, courtesy of Anssi.

Day 4 and Day 5

Thursday and Friday were dedicated to the Devices and Sensor WG. The agenda was not too packed, but still kept us busy for one and a half days. We discussed almost from the start about permissions and how they should be handled. Permissions are a big topic in Project Fugu 🐡 and I'm happy that there's work ongoing in the TAG to improve the situation, including efforts around the Permissions API that is unfortunately not universally supported, leading to inconsistencies with some APIs having a static method for getting permission, others asking for permission upon the first usage attempt, and yet others to integrate with the Permissions API. For the Geolocation Sensor API, we agreed to try retrofitting expressive configuration of foreground tracking into the Geolocation API specification instead of doing it in Geolocation Sensor, which should improve vendor adoption. For geofencing and background geolocation tracking, we decided to explore Notification Triggers and Wake Locks respectively, which both weren't options when the work on Geolocation Sensor was started initially.

Maryam Mehrnezhad, an invited expert in the working group whose research is focused on privacy and security, presented on and discussed with us the implications on both fields that sensors potentially have and whether mitigation like accuracy bucketing or frequency capping are effective. The minutes capture the conversation well.

Finally, we changed the surface of the Wake Lock API hopefully for the last time. The previous changes just didn't feel right from a developer experience point of view, so better change the API while it's behind a flag than be sorry forever. I reckon I do feel sorry for the implementors Rijubrata Bhaumik and Raphael Kubo da Costa… 🙇

partial interface Navigator {
[SameObject] readonly attribute WakeLock wakeLock;

partial interface WorkerNavigator {
[SameObject] readonly attribute WakeLock wakeLock;

interface WakeLock {
Promise<unsigned long long> request(WakeLockType type);
Promise<void> release(unsigned long long wakeLockID);

dictionary WakeLockEventInit {
required unsigned long long wakeLockID;

interface WakeLockEvent : Event {
constructor(DOMString type, WakeLockEventInit init);
readonly attribute unsigned long long wakeLockID;

As a general theme, we "hardened" a number of APIs, for example decided to integrate geolocation with Feature Policy and now require a secure connection for the Battery Status API. The chairs Anssi and Reilly Grant have scribed the one and a half days brilliantly, the minutes for day 1 and day 2 are both online.


As I wrote in the beginning, TPAC slowly starts to feel like a venue where I can make some valuable contributions. Rowan Merewood put it like this in a tweet:

The biggest thing I'm learning at [#W3Ctpac] is if you want to change the web, it's a surprisingly small group of people you need to convince. The surrounding appearance of the W3C and all its language is intimidating, but underneath it's just other human beings you can talk to.

To which Mariko Kosaka fittingly responds:

[Y]eah, but let's not forget getting to talk to that small set of people most often comes with being very, very, very privileged. […]

It's indeed a massive privilege to work for a company that has the money to take part in W3C activities, fly people across the world, and let them stay in five star conference hotels. With all the love for the web and all the great memories of a fantastic TPAC, let's not forget: the web is threatened from multiple angles, and being able to work in the standards bodies on defending it is a privilege of the few. Both shouldn't be the case.

You can edit this page on GitHub.


1 Mention

Back in March 2003, Nick Finck and Steven Champeon stunned the web design world with the concept of progressive enhancement: Rather than hoping for graceful degradation, [progressive enhancement] builds documents for the least capable or differently capable devices first, then moves on to enhance those documents with separate logic for presentation, in ways that don't place an undue burden on baseline devices but which allow a richer experience for those users with modern graphical browser software. While in 2003, progressive enhancement was mostly about using presentational features like at the time modern CSS properties, unobtrusive JavaScript for improved usability, and even nowadays basic things like Scalable Vector Graphics; I see progressive enhancement in 2020 as being about using new functional browser capabilities. Sometimes we agree to disagree ⚓ Feature support for core JavaScript language features by major browsers is great. Kangax' ECMAScript 2016+ compatibility table is almost all green, and browser vendors generally agree and are quick to implement. In contrast, there is less agreement on what we colloquially call Fugu 🐡 features. In Project Fugu, our objective is the following: Enable web apps to do anything native apps can, by exposing the capabilities of native platforms to the web platform, while maintaining user security, privacy, trust, and other core tenets of the web. You can see all the capabilities we want to tackle in the context of the project by having a look at our Fugu API tracker. I have also written about Project Fugu at W3C TPAC 2019. To get an impression of the debate around these features when it comes to the different browser vendors, I recommend reading the discussions around the request for a WebKit position on Web NFC or the request for a Mozilla position on screen Wake Lock (both discussions contain links to the particular specs in question). In some cases, the result of these positioning threads might be a "we agree to disagree". And that's fine. Progressive enhancement for Fugu features ⚓ As a result of this disagreement, some Fugu features will probably never be implemented by all browser vendors. But what does this mean for developers? Now and then, in 2003 just like in 2020, feature detection plays a central role. Before using a potentially future new browser capability like, say, the Native File System API, developers need to feature-detect the presence of the API. For the Native File System API, it might look like this: if ('chooseFileSystemEntries' in window) { // Yay, the Native File System API is available! 💾 } else { // Nay, a legacy approach is required. 😔 } In the worst case, there is no legacy approach (the else branch in the code snippet above). Some Fugu features are so groundbreakingly new that there simply is no replacement. The Contact Picker API (that allows users to select contacts from their device's native contact manager) is such an example. But in other cases, like with the Native File System API, developers can fall back to <a download> for saving and <input type="file"> for opening files. The experience will not be the same (while you can open a file, you cannot write back to it; you will always create a new file that will land in your Downloads folder), but it is the next best thing. A suboptimal way to deal with this situation would be to force users to load both code paths, the legacy approach and the new approach. Luckily, dynamic import() makes differential loading feasible and—as a stage 4 of the TC39 process feature—has great browser support. Experimenting with browser-nativefs ⚓ I have been exploring this pattern of progressively enhancing a web application with Fugu features. The other day, I came across an interesting project by Christopher Chedeau, who also goes by @Vjeux on most places on the Internet. Christopher blogged about a new app of his, Excalidraw, and how the project "exploded" (in a positive sense). Made curious from the blog post, I played with the app myself and immediately thought that it could profit from the Native File System API. I opened an initial Pull Request that was quickly merged and that implements the fallback scenario mentioned above, but I was not really happy with the code duplication I had introduced. As the logical next step, I created an experimental library that supports the differential loading pattern via dynamic import(). Introducing browser-nativefs, an abstraction layer that exposes two functions, fileOpen() and fileSave(), which under the hood either use the Native File System API or the <a download> and <input type="file"> legacy approach. A Pull Request based on this library is now merged into Excalidraw, and so far it seems to work fine (only the dynamic import() breaks CodeSandbox, likely a known issue). You can see the core API of the library below. import { fileOpenPromise, fileSavePromise, } from ''; (async () => { // This dynamically either loads the Native // File System API or the legacy module. const fileOpen = (await fileOpenPromise).default; const fileSave = (await fileSavePromise).default; // Open a file. const blob = await fileOpen({ mimeTypes: ['image/*'], }); // Open multiple files const blobs = await fileOpen({ mimeTypes: ['image/*'], multiple: true, }); // Save a file. await fileSave(blob, { fileName: 'Untitled.png', }); })(); Polyfill or ponyfill or abstraction ⚓ Triggered by this project, I provided some feedback on the Native File System specification: #146 on the API shape and the naming. #148 on whether a File object should have an attribute that points to its associated FileSystemHandle. #149 on the ability to provide a name hint for a to-be-saved file. There are several other open issues for the API, and its shape is not stable yet. Some of the API's concepts like FileSystemHandle only make sense when used with the actual API, but not with a legacy fallback, so polyfilling or ponyfilling (as pointed out by my colleague Jeff Posnick) is—in my humble opinion—less of an option, at least for the moment. My current thinking goes more in the direction of positioning this library as an abstraction like jQuery's $.ajax() or Axios' axios.get(), which a significant amount of developers still prefer even over newer APIs like fetch(). In a similar vein, Node.js offers a function fsPromises.readFile() that—apart from a FileHandle—also just takes a filename path string, that is, it acts as an optional shortcut to, which returns a FileHandle that one can then use with filehandle.readFile() that finally returns a Buffer or a string, just like fsPromises.readFile(). Thus, should the Native File System API then just have a window.readFile() method? Maybe. But more recently the trend seems to be to rather expose generic tools like AbortController that can be used to cancel many things, including fetch() rather than more specific mechanisms. When the lower-level primitives are there, developers can build abstractions on top, and optionally never expose the primitives, just like the fileOpen() and fileSave() methods in browser-nativefs that one can (but never has to) perfectly use without ever touching a FileSystemHandle. Conclusion ⚓ Progressive enhancement in the age of Fugu APIs in my opinion is more alive than ever. I have shown the concept at the example of the Native File System API, but there are several other new API proposals where this idea (which by no means I claim as new) could be applied. For instance, the Shape Detection API can fall back to JavaScript or Web Assembly libraries, as shown in the Perception Toolkit. Another example is the (screen) Wake Lock API that can fall back to playing an invisible video, which is the way NoSleep.js implements it. As I wrote above, the experience probably will not be the same, but the next best thing. If you want, give browser-nativefs a try.