 Hi, my name is Thomas Steiner, and I work as a developer relations engineer on the Google Chrome team. Today, I want to present on bringing Adobe's Creative Cloud to the web, starting with Photoshop. Before joining the developer relations team, I worked as a Google web solutions consultant. Whenever I was asked by one of our partners if they should be building for the web, I would always say yes, unless you're building something like Photoshop. I used Photoshop as a generic example for an app that I could never imagine to be built on the web. Boy was I wrong. At last year's Mac conference, Adobe blew everyone's minds, including mine when they showed a version of Photoshop running in the browser. Here's a quick walkthrough created by Adobe that gives you an impression. You probably have expected it. Photoshop is powered by WebAssembly. Adobe used M-scripten, a complete compiler toolchain to WebAssembly to compile their existing Photoshop code base. Some of the code may be just as old as Photoshop itself. As you have seen in the demo reel, friction-free access to Photoshop by just clicking a link is an elementary part of Adobe's vision. Adobe wants Photoshop to work on all browsers and to work with vendors like Apple, Mozilla, and Google to make it happen. For now, Universal Browser support is still pending, but we're getting closer with every new release of Safari, Firefox, or Chrome. And we really love how Adobe drives the web forward. Before I dive into the talk, I want to highlight that Photoshop is obviously production software. And reading minified production code is hard. You can try reading the code on the slide that I have pulled from the app if you don't believe me. So to explain how Photoshop managed to get Photoshop on the web, I'll be using simplified example snippets to get the points across. None of what you're going to see exists in a Photoshop code base, but the main mechanics work as illustrated. If you want to follow along, I've created a repo with all WebAssembly examples on my GitHub. Note that I will talk about process support at the end of the Wasm blog. And with all this out of the way, let's start. The first topic is multithreading for dynamic multitasking with P-threads. As you can imagine, Photoshop consists of more than one thread, so multithreading is a central phone app like that to work on the web. One concrete example of a Photoshop uses P-threads is certain pixel operations. The core idea is to split up an image into tiles, run some algorithm in parallel on each tile in a P-thread, and then to reassemble the tiles back into the resulting image. You may remember threads as the smallest sequence of programmed instructions that can be managed independently by a scheduler. Threads are components of processes. The multiple threads of the given process may be executed concurrently. Unscriptedness support for multithreading using shared array buffer. This API allows for memory to be shared between the main thread and web workers. It also supports atomic operations for synchronization. For shared array buffer to work, sites need to set the cross-origin opener policy and the cross-origin embedder policy headers. Learn more about these headers in an article on web.dev. So how does multithreading with Unscripted work now? Let me dive into some C code. First, I have a thread function that just prints when the thread is done. Second, I create and start two threads with a P-thread create function. The P-thread create function starts a new thread in the calling process. Finally, I await both threads with a P-thread join and then print done. The P-thread join function waits for the specified thread to terminate. To compile this code with Unscripted compiler frontend EMCC, I need to pass the use P-threads parameter and set a P-thread pool size, in this case, of size two. The next topic I'm going to talk about is C++ exception handling. You may be surprised to learn that WebAssembly initially didn't really have Basm level exception handling, but only JavaScript level exception handling with lots of overhead. For a massive software project like Photoshop, exceptions are of course crucial. When a code throws an exception, it breaks the control flow, which can then be handled. Exceptions can be thrown inside of a Basm module or be thrown somewhere in an imported module. Unscripted now has built-in exception handling support based on the exception handling proposal. Let me show you some C++ code. All its main function does is throw an exception. I can enable the built-in Basm level exception handling in Unscripted by passing the F-Basm exception parameter to EMCC. Without the parameter, the entire Basm program would be terminated. Up next, I'm going to talk about SIMD instructions. SIMD stands for Single Instruction, Multiple Data. SIMD instructions are used heavily in Photoshop. For example, consider increasing the brightness of an image. For each pixel, for each quadruple consisting of RGB and alpha, the new value is calculated based on a parallelizable formula. As I said, SIMD stands for Single Instruction, Multiple Data, which exploits data parallelism in applications like Photoshop. You can read out more about the theory in an in-depth article written by the V8 team. SIMD operations are supported on most modern CPU architectures, although each of them in a different way. They're exposed in a specific Basm SIMD 128 header file, which defines a reduced set of operations that can be widely supported on current hardware. This includes operations on fixed-width 128-bit data represented through a new V128 value type. You can see an example function here that makes use of the Vasm V128 load function for a SIMD multiplication operation, which stores the product by the Vasm V128 store function. To enable SIMD operations, I passed the M-SIMD 128 parameter to EMCC. Whoo! With so many new Vasm features like SIMD, P-threads, and exception handling, you may be wondering about browser support. Turns out, feature detection for progressively enhanced Vasm is a very important concept to be aware of. We have summarized all the details in an article on web.dev, but in the next few slides, I want to quickly summarize the main ideas. It all starts with the shortest possible C or C++ programs that tell a developer if a given feature is supported. This example shows feature detection for SIMD. We've collected all of these feature tests in a library, so we can import the ones you're interested in and in JavaScript await their outcome. This allows you to import exactly the right set of Vasm modules that a given browser supports. Of course, this requires you to have modules for all possible feature combinations ready. Here, you can see how we compile the different modules with the EMCC parameters and flags for the various features. Finally, in each Vasm module, you can also work with if-dev directives and provide implementations based on them. If you're curious to see what Vasm features your browser supports, there's a testing page where you can see the support situation of your current browser and compare it to fixed versions of Firefox, Safari, and Chrome. With WebAssembly powering so much a Photoshop, it's clear that proper debugging support was needed. In the last couple of months, the Chrome DevTools team have greatly improved the developer experience of DevTools for Vasm developers. At the scale of apps like Photoshop, logging errors to the console only gets you that far. We have documented the full flow of debugging WebAssembly with modern DevTools on developer.chrome.com, but today I want to provide just a quick sneak peek. For now, developers need to install a special Chrome extension for C and C++ DevTools support in the Dwarf format. Dwarf is a widely used standardized debugging data format. Next, you need to toggle on a DevTools experiment that enables Dwarf support for WebAssembly. Now, you're finally ready for the first test program. This C program sends the SIG-ABRT signal via the abort function if the size check of the two variables X and Y is true. SIG abort in C is a signal for abnormal program termination. I compile this program with the G flag, which enables debugging support. When I then execute the Vasm code and have the pause code exceptions checkbox checked, I can debug the scope variables and the call stack in Vasm, almost as if they were regular JavaScript code. The DevTools team has also added rich type debugging support. Definitely check out the previously mentioned article for all details and watch the DevTools State of the Union talk for some more Vasm debugging tips. Wow, this was a lot of WebAssembly. Up next, I want to talk about the next enabling technology, files. In this case, not regular files that you would expect to find on disk, but files that are private to the origin of HTTPS Photoshop.adobe.com. These files have high-performance read and write behavior via access handles. Photoshop files can become really huge. Files as if more than a gigabyte are not unheard of. With multiple browser tabs open and other apps running, you can imagine that this causes a problem when Photoshop image data no longer fits into RAM. The solution to this in computer science generally is memory paging. Memory paging is a memory management scheme where a computer stores and reviews data from a secondary storage for use in the main memory. Photoshop uses something like swap files with access handles that are private to the origin of the app for moving data between disk and RAM in a performant way through web workers. You can read up on all the details about these files in an article on web.dev. I start with creating a root directory by calling navigator.storage.get directory. From this root, I can then get a file handle via root.get file handle. In a web worker context, I can then get an access handle with a create sync access handle method. This then allows me to read and write data from a buffer with maximum performance. One other example where this API can shine about from swap files is creating custom database implementations that can use access handles for their file-based backend. Access handles and the origin private file system are supported by Chromio-based browsers on desktop and mobile like Chromo Edge and also on Safari or iOS and Micro S. Up next, I want to talk about the P3 color space for colors beyond sRGB. sRGB is a standard red, green, blue color space created by HP and Microsoft in 1996 used to monitors, rentals, and the web. You may know sRGB colors like Rebecca Purple which is expressed as 663399 in hex or everyone's favorite, deep pink, FF1493 in hex. When you look at deep pink in an sRGB color picker, you might be tempted to think that there is no pink beyond deep pink since the color appears to be at the boundary of the space. But the truth is there are a lot more possible colors that simply can't be expressed with sRGB. If you look at the simplified CIE-1931 color space chromaticity diagram, you can see the inner blue triangle that denotes the colors addressable with sRGB and the larger outer green triangle that denotes the colors addressable with P3. Photoshop, of course, wants enable creatives to use as many colors as possible which is why they support the P3 color space. If you look at deep pink in a color picker that uses a different color space, like in this case, safaris, that uses the P3 color space, you can see that deeper pinks than deep pink are possible. Deep pink is now expressed using the CSS color function and the display P3 identifier with the three color space parameters. The exact theory of P3 is beyond the scope of this talk but if you look at the deepest possible deep pink addressable in P3, you can see that it's expressed as one for the red, zero for the green and point five for the blue component. To check if your screen supports P3, you can open up a test image like the one on this slide. On supporting devices, you should see a little Android robot. I've added a link and a recolored version of the image in case it's hard to see on the video stream. You can use P3 colors as a progressive enhancement. Here, I first define a CSS custom property called deepest pink as deep pink in the sense of the sRGB color space. If the device supports it, I redefine the custom property using the P3 color space with the CSS color function. I can then use the color by the CSS variable and it will always be the deepest technically possible pink. The P3 color space with the CSS color function is currently supported in Safari on iOS and Mac OS but support in Chromium browser is coming. You can track progress by storing the relevant Chromium bug. You can now also define an explicit color space for use in Canvas 2D contexts. Before, it was implicitly assumed to be sRGB but with the color gamut media query, you can now check support and pass the appropriate color space as a parameter to the Canvas elements get context method. The next big topic is web components. With Photoshop being one of many apps in Adobe's Creative Cloud, you can imagine that it's desirable for all these apps to use the same design language. Reusable web components make this very much possible. Adobe have open sourced a lot of the web components they use in Photoshop under the spectrum web components brand. The page, of course, is built with spectrum web components. When you inspect Photoshop with a web components DevTools extension, you can see that the app is composed of many web components. For example, the open and desktop button is an SP button. Inspecting a DOM tree of the SP button element reveals that SP button and almost all other components on the page are built with lit. You can tell by the signature lit plus a random number of comments that are part of the secret of what makes lit so efficient when it needs to make changes to the DOM. Read up more on lit and why it's so fast on lit.dev. The quick summary is that lit uses these comments to keep track of its expression and parts logic locations so it can update efficiently with the minimum amount of DOM changes. Lit produces standard web components that are supported in all browsers except the old Internet Explorer. What makes Photoshop performant is its heavy use of service workers. While it's currently not an offline enabled app, Photoshop still highly profits from service workers through the caching of its assets. Users pay the cost of downloading only once and from there on can reuse the pre-cached optimized resources. If you look at the actual service worker code of the app, you will see an array that looks something like the simplified version on this slide. One URL in there that sticks out is one for a Basm file. When I downloaded this Basm file to disk, my mind was blown by the sheer size of this file, more than 80 megabyte of Basm. But thanks to service workers, this file needs to be only downloaded once and is then stored in an optimized way with persisted bytecode and pre-parced jit cache. Photoshop makes use of the pre-caching module in Workbox. Workbox makes dealing with service workers and caching really easy. Be sure to check out the documentation of the pre-caching module on developer.chrome.com. And just in case you were wondering, service workers are supported in all browsers except the old Internet Explorer. So, thinking back, boy, the web has come a long way. I couldn't imagine an app like Photoshop on the web. And here I am, making edits to graphic assets in just the next browser tab. Photoshop on the web was enabled by two big developments. New web capabilities supported in part by Project Fugu and Vasem improvements and tooling support in Abscripten and DevTools. Starting with Vasem and Abscripten, we've seen multithreading through P-threads, exception handling, SIMD instructions, and Vasem debugging in Chrome DevTools. When it comes to the new web capabilities, there were file handles in the origin private file system, the P3 color space, web components through LIT and service workers through Workbox. If you have an Adobe Creative Cloud subscription, be sure to check out the app at Photoshop.Adobe.com. Read our in-depth article, Photoshop's journey to the web, and learn about what Adobe have done. Try some of the new APIs and Vasem features from this talk, and be part of our and Adobe's mission to push the web forward. And with that, thank you very much for watching. My name is Tom. You can find me as Tomajak on Twitter and most other places on the Internet. Enjoy the rest of IO.