Development of a web designer tool based on a web application (Figma). Transfer

Our understanding of the future of design tools is such that tools and content should be easily accessible.
That's why we created Figma, a command-line interface design tool, as a cloud service distributed as a web application.
When we decided to create Figma, we knew that it would be a serious challenge.
To truly succeed, you must provide a high-precision editing tool that will be accepted by professionals and will work equally well in any environment.
The road to results was very difficult; as a result, we practically created a browser inside the browser.

The reason for the difficulty of this task was that the web was not created as a general-purpose computing platform. The web began as a document-oriented technology, to which a whole bunch of good stuff was added for application development.
This good usually was in the form of specific APIs with a rather narrow focus, instead of giving general purpose primitives that could be used to implement all sorts of things.
A few examples:
- CSS provides a number of excellent text rendering / layout algorithms , but it doesn’t allow you to customize them or get the result of what the browser did to use the text layout algorithm as part of another algorithm.
- All browsers provide a high-performance GPU linker , but in a web environment, there is no way to cling to the rendering algorithm and change the linker behavior to add performance optimizations or special blending modes.
- Browsers have built-in highly optimized image decoders that decode them asynchronously outside the UI stream using the hardware, but there is no API to pass parameters to decoders that allow, for example, to take into account EXIF orientation or to prevent the setting of an incorrect color space when using drawImage and getImageData.
The situation with the lack of common primitives on the web is starting to change, and now there are technologies such as WebGL and asm.js that give developers tools to work with hardware directly, bypassing the browser engine. This is a conquest that finally makes high-performance web-based graphical applications something realistic and practical. Developers no longer need to wait until the feature they need is implemented in browsers, they can create such functionality on their own!
Emscripten
Our editor is written in C ++ and cross-compiled in JavaScript using the emscripten cross-compiler . The emscripten compiler targets asm.js , a subset of JavaScript supported in all modern browsers that allows you to get predictable, compact machine code from the JavaScript JIT compiler.
This approach has several advantages:
- We completely control the memory structure and can use compact 32-bit floating-point numbers or even bytes, when appropriate, instead of 64-bit doubles in JavaScript. This is very important for applications like ours that use large amounts of data.
- The generated code fully controls the allocation of memory, which greatly simplifies the task of getting UI rendering at 60 frames per second, avoiding pauses from garbage collection. All C ++ objects are placed in reserved ranges in a pre-allocated typed array, so there is simply nothing to do with the JavaScript garbage collector.
- The generated code is pre-optimized using the advanced LLVM compiler. Combining this with C ++ template specialization, we get a very efficient code that is only up to 2 times slower than the native one.
- It is also guaranteed that the asm.js code is free from deoptimization points, so that the JIT compiler can perform AoT compilation and provide predictable performance. Regular JavaScript code goes through the heuristic algorithms of the JIT compiler, so its performance can sometimes wilderly vary between consecutive runs of the same code.
I do not mean that emscripten is perfect. As with any new shit, there were plugs along the way. One of the problems for us was that some browser configurations could not allocate large ranges of continuous address space for a huge typed array that contains the entire emscripten address space. The worst case was 32-bit Chrome for Windows , which sometimes could not allocate even a 256MB typed array, because ASLR fragmented address space. Since then it has already been fixed.
Another feature that helps is the use for large resources of handles for removing things from the heap such as buffers for pictures and geometric objects. We have created an internal IndirectBuffer API (we have opened up here), which allows you to reference an external typed array and makes it available in C ++. Removing large objects from the main heap reduces memory fragmentation problems for long-playing sessions, allowing us to use more limited address space in 32-bit browsers, and allowing us to overcome the 31-bit limit on the size of a typed array in 64-bit browsers.
Now asm.js is already very well supported, but there are a lot of cool things coming up.
WebAssembly is an attempt to implement a binary format for asm.js code to significantly reduce the parsing time that all major browsers have joined. Right now, the only form of multithreading is the use of messaging web workers, but the specificationa common typed array (on the way) will make multithreading with shared memory a reality.
Rendering
We created our own rendering engine to ensure that content is rendered quickly and uniformly on all platforms. Browsers have great mechanisms for working with graphics, and we initially tried to use them, instead of creating a new rendering engine. Without low-level APIs to access the browser rendering tree, the available options were HTML, SVG, or 2D canvas.
None of these options were satisfactory for a number of reasons:
- HTML and SVG carry a lot of excess baggage and often work much slower than the 2D canvas API due to the use of the DOM. They are usually optimized for scrolling, rather than zoom, and the geometry is often recounted with every zooming.
- There is no guarantee that the rendering will pass with hardware acceleration through the GPU, and many things are still rendering on the CPU.
- Support for masking, blurring, and blending modes in HTML and SVG varies wildly from browser to browser, and often does not smooth, or the output is too low resolution on high-resolution displays.
- The 2D canvas API is a real-time API, not a delayed one, so all geometric objects must be reloaded in the GPU for each frame. This behavior is wasteful without need, and can become a bottleneck.
- The layout of the text is not the same between different browsers , and not only between versions of the same browser for different platforms.
- We wanted to be able to add features such as drawing a carbon gradient , which is not supported by any of the rendering APIs.
Instead of trying to get one of this to work as it should, we implemented everything from scratch using WebGL. Our converter is a highly optimized, tile-based engine with support for masks, blurring, uneven gradients, blending modes, transparency in nested layers, and much more. All conversions are done on the GPU and are fully anti-aliased.
Our code looks almost like a browser inside a browser; we have our own DOM, our own linker, our own text layout engine, and we are thinking of adding a transformation tree, just like what is used to display HTML in browsers.
Browsers
The functionality of the web platform is still catching up with native platforms and, unfortunately, there are a number of gaps here and there. As long as we don’t have the resources to fill in some large gaps, I am still trying to fix what I can when it makes sense.
Before I started working on Figma, custom high-DPI cursors were really broken on the web. I had to repair Chrome , Fox and Webkit myself , because they were all broken in their own way. There is still no one general way to work with this (SVG in Fox, -webkit-image-set in Chrome and Webkit, and the prehistoric .cur format in IE), but at least now everything works.
I also fixed some glaring performance and usability bugs to make our product better. The web can sometimes freak out, but browsers are not black boxes (well, except that browser itself), and often all it takes to fix an annoying bug on the web is half a day of fuss in the browser code, a day of wandering around about the patch, and several months of waiting for the release containing the patch.
There is much more that the web platform could improve to help improve Figma:
- Our greatest pain is the lack of access to character contours and kerning tables, which at the moment cannot be obtained at all . One of the main concerns is the security issue, but the battle has already been lost . We hope that access to the font data will be open at least in the mode of requesting permission from the user, as well as other APIs sensitive to personal information. The Khromovites proposed a way to fix this, which they are now working on , but no one is looming on the horizon.
- We would gladly add support for pasting content in popular formats from the clipboard (.ai, .pdf, etc.), but the web does not allow this . The only formats in the specifications are text / plain and text / html (our Figma uses text / html with binary data encoded in HTML comments).
- Another problem for us is the lack of pinch gesture support for the trackpad in OS X. Khromovtsy added a little-known hack in which the pinch gesture sends the mouse wheel event with ctrl held down and calls preventDefault (), which allows the page to handle this. This is awesome and makes the magnification and panning in Figma feel native. I tried to add this to the Fox , but this attempt is still stuck. The pinch in Safari works like an increase, which confuses users and this cannot be disabled .
Conclusion
Performance and quality are one of our most important qualities. They are slightly different from normal qualities, because you pay attention to them, first of all, in their absence, but they do the whole trick.
This article is a translation of a 2015 article from the Figma team blog. Although the article is not new, it is still quite relevant. This is the first article in the Figma team blog translation cycle. The author of the translation is Sergey Durnov .