Our vision for the future of design tools is one where both the tool and the content are easily available to anyone, anywhere. That’s why we built Figma, a collaborative interface design tool that we recently launched, as a browser-based cloud service. When we set out to build Figma we knew it would be a challenge. To really succeed it would have to provide a high-fidelity editing experience that professionals would accept and would have to work consistently everywhere.
Pulling this off was really hard; we’ve basically ended up building a browser inside a browser.
The reason this is hard is because the web wasn’t designed as a general-purpose computing platform. It started off as a technology primarily for documents and had a whole bunch of stuff for application development bolted on top. That stuff usually took the form of specific APIs for one-off cases instead of providing general primitives that can be used to implement all sorts of things. Some examples:
This lack of general primitives on the web is starting to change and now there are technologies like WebGL and asm.js that let developers cut past the browser and talk directly to the hardware. It’s this advancement that’s finally made high-performance web-based graphics applications practical. Developers no longer need to wait for the exact features they need to be added to the web, they can build those features themselves!
That’s not to say emscripten is perfect. As with any new technology, there were bumps along the road. One big issue for us was that certain browser configurations couldn’t allocate large ranges of continuous address space for the huge typed array that contains the entire emscripten memory space. The worst case was 32-bit Chrome on Windows which sometimes couldn’t even allocate a 256mb typed array because ASLR was fragmenting the address space. This has since been fixed.
A trick that also helps is to use handles to out-of-heap data for large resources such as image and geometry buffers. We have an internal API called IndirectBuffer (which we are open sourcing here) that references an external typed array and makes it available to C++. Moving large allocations out of the main heap reduces memory fragmentation issues for long-running sessions, allows us to use more of the limited address space in 32-bit browsers, and allows us to break past the 31-bit typed array size limitation in 64-bit browsers.
Current asm.js support is already quite good but there’s more exciting changes coming up. WebAssembly is an effort to implement a binary format for asm.js code to drastically reduce parse time that all major browser vendors are on board with. Right now the only form of multithreading is using web workers with message passing, but the upcoming shared typed array specification will make shared-memory multithreading a reality.
We’ve implemented our own rendering engine to make sure content renders quickly and consistently across platforms. Browsers have amazing graphics implementations and we initially attempted to use them instead of building a new rendering engine. Without a low-level API to access the browser’s render tree, the available options are either HTML, SVG, or 2D canvas. None of these options were satisfactory for many reasons:
Instead of attempting to get one of these to work, we implemented everything from scratch using WebGL. Our renderer is a highly-optimized tile-based engine with support for masking, blurring, dithered gradients, blend modes, nested layer opacity, and more. All rendering is done on the GPU and is fully anti-aliased. Internally our code looks a lot like a browser inside a browser; we have our own DOM, our own compositor, our own text layout engine, and we’re thinking about adding a render tree just like the one browsers use to render HTML.
The functionality of the web platform is still catching up to native platforms and there are unfortunately still a few gaps here and there. While we don’t have the resources to fill some of the larger gaps, I still try to fix what I can when it makes sense.
Before I started working on Figma, high-DPI custom cursors were really broken on the web. I had to manually fix Chrome, Firefox, and WebKit because all of them were broken in different ways. There still isn’t a single unified way to do it (SVG for Firefox, -webkit-image-set for Chrome and WebKit, and the ancient .cur format for IE) but at least it’s possible now.
I’ve also fixed a few egregious performance and usability bugs to make our product better. The web can be frustrating sometimes, but browsers aren’t a black box (well, except for that browser) and often all it takes to fix annoying issues with the web is an afternoon of poking around in browser code, a day of back-and-forth over a patch, and a few months of waiting for the release to roll out.
There’s still more that the web platform could do that would make Figma even better:
Performance and quality are some of our most important features. They’re a little different than normal features because you only notice them when they aren’t there, but they make all the difference.
I’m very excited to finally reveal Figma to the world. The product isn’t open to the public yet but you can sign up for our waiting list and try it out soon. Let us know what you think!