We want a future design tool to be like this: anyone, anywhere, can easily use it and get the content produced by it. So we created figma as a browser based cloud service that can provide collaborative design capabilities. When we started working on figma, we understood that it was a challenge. To accomplish this challenge, we need to provide a near original editing experience so that designers will accept the tool and work with it anywhere.
It's really hard to do this. Finally, we basically built a new browser inside the browser.
It’s difficult because the web is not designed as a general computing platform. It is mainly for the processing of documents, it is bound to a set of things for the development of applications. These things usually use specific APIs for specific scenarios, instead of providing a common interface that can be used to implement various things. For example:
- CSS has a lot of fancy text layout algorithms, but it can not customize the layout algorithm, and can not analyze the text layout process, so the text layout algorithm can not be used in other algorithms.
- All browsers provide high-performance GPU synthesizer, but on the web platform, there is no way to access the rendering algorithm, so you can’t change the way the synthesizer works to add performance analysis or custom mixed mode functions.
- The browser has a highly optimized image parser that works outside the UI thread and uses the latest hardware features. However, there is no API that can pass parameters to these parsers to implement such functions as “processing EXIF direction” or “avoiding touching the color space when using the methods of DrawImage and getimagedata”.
The lack of a common underlying interface for the web platform is now changing because webgl and asm.js This technology allows developers to bypass browsers and talk directly to hardware. Finally, technological progress makes it possible to apply high-performance graphics based on Web platform. Developers don’t have to wait for the web platform to add the extra features they need, they can build them themselves!
- We control the memory layout completely by ourselves, and we can use small 32-bit floating-point numbers, sometimes even bytes, instead of JS 64 bit double precision floating-point numbers. This is crucial for apps like ours that need to use huge amounts of data.
- The built code controls the memory allocation completely, which can avoid the jam caused by GC and make it easier for the application to reach 60 frames. All C + + objects are within a preallocated reservation, so the GC of JS will never intervene.
- The generated code is pre optimized by llvm’s advanced optimizer. This, combined with specialized C + + templates, produces efficient code that is twice or more better than native performance.
- be-all asm.js No code needs to be optimized again, so jits can precompile it and provide predictable performance. However, conventional JS code relies on JIT for optimization, so the performance of the same code may vary greatly later.
This is not to say that emscripten is perfect. Like any new technology, the way forward is bumpy. The big problem we’re asking is that some browser settings can’t allocate a large number of contiguous memory addresses for emscript. The worst example is 32-bit chrome on windows. Sometimes we can’t even allocate a 256M typedarray because ASLR segments the memory address. This problem has now been solved.
Another trick to help is to use references to large resources outside the heap, such as images or buffer fragments. We have an internal API called indirectbuffer to represent an external typedarray and make it available in C + +. Moving large memory allocation out of the main heap reduces memory fragmentation in long-term sessions, allows us to use more memory in restricted 32-bit browsers, and allows us to break through the 31 bit typedaray size limit of 64 bit browsers.
Right now asm.js The support is good, but some more exciting changes are coming. Web assembly is an implementation asm.js A result of binary form, which can be greatly reduced asm.js Code parsing time, and all browsers are ready to implement it. At present, the only way to use multiline is to use web worker and send messages through post message. But the coming shareable typedarray standard will make it possible to share memory among multi threads.
We implemented our own rendering engine to ensure that the content is rendered quickly and consistent across multiple platforms. Browsers have amazing graphical implementations, and we didn’t plan to build our own rendering engine at first. Without the underlying API to get the browser render tree, only HTML, SVG or 2D canvas can be selected. But they can’t meet our needs for the following reasons:
- HTML and SVG have a lot of burden, which is usually much slower than 2D canvas due to the reason of getting dom. This is usually optimized for scrolling rather than zooming, and geometry is usually redrawn every time the zoom changes.
- GPU acceleration has no guarantee, and many things are still rendered on the CPU, which is very slow in specific scenarios.
- On HTML and SVG, support for masking, blurring, and blending modes varies greatly from browser to browser, and is generally not antialiasing or too low resolution on high DPI displays.
- The 2D canvas API is an immediate mode API rather than a reserved mode API, so all collection shapes have to be re uploaded to the graphics card at each frame. This is an unnecessary waste and a potential performance bottleneck.
- The text layout on different browsers is not consistent, even in the same browser on different platforms.
- We want to add features like “angular gradients” that are not supported by any rendering API.
Instead of trying to make one of them available, we implemented everything from scratch with webgl. Our renderer is a highly optimized tile based architecture engine that supports masking, blurring, jitter angle, blending mode, nesting transparency, and so on. All rendering is done on the GPU and is completely anti aliased. Deep into the code, especially like a browser inside a browser. We have our own DOM, our own synthesizer, our own text layout engine, and we are considering adding a rendering tree like that used by browsers to render HTML.
The capabilities of web platforms are still catching up with native platforms, but unfortunately there are still a few gaps there. Although we don’t have the resources to fill those huge gaps, I’ve filled in what I can while it still makes sense.
Before I went to figma to work, high DPI custom mouse was completely broken on the web platform. I had to fix chrome, Firefox and WebKit one by one, because they didn’t work the same way. So far, there is no unified way to solve this problem, but at least it is possible.
I also fixed several bad performance and usability bugs to make our product better. The web platform is sometimes frustrating, but the browser is not a black box. The cost of fixing some annoying problems on the web is usually to spend an afternoon debugging the browser’s code, focusing on a patch in a day, or waiting for a new version for several months.
There are still some things that can be done on the web platform to make figma better:
- Our biggest pain point is that we can’t touch the glyph outline and the “kerning tables”. So far, there is no way to solve this problem. One of our main concerns is fingerprints. The fighters have lost the battle. We want to be able to get font data, which is similar to the privacy terms API when users agree to a protocol similar to ours. Chrome is currently working on a proposal to solve this problem, but when other browsers will be able to solve it, it’s hard to forget.
- We want to add common clipboard formats, such as. AI,. PDF, etc., but the web platform can’t. The only clipping format is text / plain or text / HTML. Our figma clipping format is a text / HTML format with binary data annotations.
- Another problem for us is the lack of support for touchpad gestures. Chrome has added a little-known trick: the touchpad can send a wheel event with the CTRL key pressed, and call preventdefault to allow the page to process it. This gives figma’s zoom a native experience. I tried to add it to Firefox, but it got stuck. The touch on safari causes zooming to be confusing to users, but there’s no way to stop it.