Our understanding of the future of design tools is such that both tools and content should be easily accessible.
That is why we created Figma, a team tool for working on interface design, 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 really succeed, you must provide a highly accurate editing tool that will be adopted by professionals, and will work equally well in any environment.
The road to the result was very difficult; as a result, we practically created a browser inside the browser.
The reason for the complexity 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 heap of good stuff for application development was later added.
This good was usually in the form of specific APIs of a rather narrow focus, rather than giving general purpose primitives that could be used to implement all sorts of things.
')
A few examples:
The situation with the lack of common primitives in the web is beginning to change, and now there are technologies such as WebGL and asm.js, which give developers tools for working with hardware directly, bypassing the browser engine. This is a conquest that finally makes high-performance graphical web applications something realistic and relevant in practice. 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 into JavaScript using the
emscripten cross-compiler. The emscripten compiler focuses on
asm.js , a subset of JavaScript supported in all modern browsers, which makes it possible to get predictable, compact machine code from the JavaScript JIT compiler.
This approach has several advantages:
- We have full control over 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 completely 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 the JavaScript garbage collector simply has nothing to do here.
- The generated code is pre-optimized using the advanced compiler LLVM. By combining this with C ++ template specialization we get very efficient code, which 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 the JIT compiler can perform AoT compilation and provide predictable performance. Regular JavaScript code runs through the heuristic algorithms of the JIT compiler, so its performance can sometimes vary the most wisely between successive runs of the same code.
I do not mean that emscripten is perfect. As with any new figovina, there were some gaps on 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 address space emscripten. The worst case was
32-bit Chrome for Windows , which sometimes could not even be allocated a 256MB typed array, since
ASLR has fragmented the address space. Since then, it has already been fixed.
Another feature that helps is the use of handles for pulling things out of the heap, such as buffers for images and geometric objects, for large resources. We have created an internal API "IndirectBuffer" (zapensorsili
here ), which allows you to refer to an external typed array and make 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 quite well supported, but there are a lot of cool stuff on the way.
WebAssembly is an attempt to implement a binary format for asm.js code to significantly reduce the parsing time, to which all major browsers have joined. Right now, the only form of multithreading is the use of web workers with message passing, but the specification of a
common typed array (on the way) will make multi-threading with shared memory a reality.
Rendering
We created our own rendering engine to ensure that the content is rendered quickly and uniformly across all platforms. Browsers have great graphics mechanisms, and we initially tried to use them, instead of creating a new rendering engine. Without low-level APIs for accessing the browser's rendering tree, the options available were HTML, SVG, or 2D canvas.
None of these options was satisfactory for a number of reasons:
- HTML and SVG drag a lot of excess baggage along and often run 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 recalculated with each zoom.
- There are no guarantees that the rendering will be hardware accelerated through the GPU, and a lot of things are still being rendered on the CPU.
- Support for blending of masks, blur and blending modes in HTML and SVG varies the most wildly from browser to browser, and often no smoothing is performed or the output is too low resolution on high-resolution displays.
- 2D canvas API is a real-time API, not deferred, so all geometric objects must be re-cast into the GPU for each frame. Such behavior is wasteful without any need, and can become a bottleneck.
- The layout of the text is not the same between different browsers , and moreover between the versions of the same browser for different platforms.
- We wanted to be able to add features such as drawing a carbon gradient that is not supported by any of the listed rendering APIs.
Instead of trying to get some 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, blur, uneven gradients, blending modes, transparency in nested layers, and more. All transformations are performed on the GPU and are completely smoothed.
Our code looks almost like a browser inside a browser; we have our own DOM, our linker, our text layout engine, and we are thinking of adding a transformation tree, just like what is used to display HTML from 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 do not 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 common way to work with this (SVG in Fox, -webkit-image-set in Chrome and Webcite, and the prehistoric .cur format in IE), but at least now everything works.
I also fixed several glaring bugs related to performance and usability to make our product better. The web can sometimes freak out, but browsers are not black boxes (well, besides the browser itself), and often all that is needed to fix an annoying bug on the web is half a day fussing in the browser code, day reeling back and forth about the patch, and a few months of waiting for the release containing the patch.
There are many more things that a web platform could improve to help improve Figma:
- Our greatest pain is the lack of access to the contours of symbols and kerning tables, which currently cannot be obtained at all . One of the main concerns is the issue of security, but the battle is already lost . We hope that access to the font data will be opened at least in the mode of requesting permission from the user, as well as other sensitive API personal information. Khromovtsy offered a way to fix it, on which they are now working , but on the horizon nobody else looms.
- We would be happy to add support for pasting content in popular clipboard formats (.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 support for the trackpad tweak in OS X. Khromovtsy added a little-known hack in which the tweak gesture sends a mouse wheel event with the ctrl pressed and causes preventDefault (), which allows the page to be controlled with this. This is cool, and makes zooming and panning in Figma native to the senses. I tried to add this to Fox , but this attempt is still stuck. A pinch in Safari works like an increase, which is confusing to users and it cannot be turned off .
Conclusion
Performance and quality are some of our most important qualities. They are somewhat different from normal qualities, because you pay attention to them, first of all, in their absence, but they do the whole thing.
This article is a translation of the 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 translation cycle of the blog of the Figma team. Translated by Sergey Durnov .