The Miro Code Block Widget: An Approach Backed by Research

Bogdan Zvyagintsev
Miro Engineering
Published in
6 min readFeb 16, 2024

In our initial post, we shared our approach for developing a code block widget that we designed based on Miro’s architecture and limitations, and the different options we considered. The second post focused on technical challenges encountered during the seamless integration of the code editor into the Miro canvas, outlining the problems we faced and the corresponding solutions. Together, these posts describe diverse aspects that led us to choose the Ace editor as a foundation and influence the overall architecture of the code block.

The Ace editor delivers a great editing experience by offering all the essential functionality, like syntax highlighting, right out of the box. But for the most part, the complexity of the code block architecture lies in rendering the Ace editor HTML output on the Miro canvas in a consistent and performant way, while supporting all the canvas-specific actions such as scaling, resizing, and switching between modes.

In this post. we provide an overview of the technical insights and architecture that eventually formed the basis of our code block. We also dive into performance analysis, offering a deeper understanding of the complexity involved in ensuring a robust and efficient solution.

Data Structure

Although storing the colorized HTML string in the database might sound tempting, due to the different technical limitations, challenges, and considerations we described in the previous blog posts, we decided to store plain text on the server. It helped us to address the following:

  • use less space and store longer code content, since we have a 6000-character limit,
  • make content transformations easier: the plain text is more suitable for changing programming language, calculating line wrapping points and resizing,
  • provide more flexibility to other types of clients and SDK applications etc, so they can choose their own representation of the code.

This is a simplified version of the data we stored for each code widget:

{
language: number,
code: string,
width: number,
height: number
lineNumbers: boolean,
fontSize: number,
}

Storing plain text meant we needed to come up with an algorithm to render properly highlighted and indented code on the canvas, without initiating a code editor (otherwise, it would have significant performance issues for boards with many widgets).

Highlighting service

We eventually settled on a special highlighting service that leverages two algorithms under the hood. The main one is based on the Ace static highlighter extension for all general use cases and a simplified version that provides only line wrappings, indentation, and line numbers but no syntax highlighting.

The second algorithm is used for cases such as resizing, which requires a lot of frequent computations, and for the initial draw when the main algorithm is not available, either not loaded yet or gradually processing other widgets.

Highlighting service receives the plain text (code) along with other parameters:

{
code: string,
language: number,
lineNumbers: boolean,
width: number,
highlighting: boolean,
}

Depending on the highlighting param, the service either performs the main or simplified algorithm.

The full picture

The whole process of using code blocks involves the following steps:

  1. Users either can create and edit the code block by launching the code editor or can resize, toggle line numbers, and change the language of the code block without starting the code editor.
  2. Then, we pass all necessary parameters to the highlighting service.
  3. And finally, the outputted HTML string is dispatched to the rendering engine.

Performance tests

The Code block widget, along with other widgets, can affect many aspects of the application performance:

  • board loading,
  • board navigation,
  • switching between widget modes,
  • widget transformations,
  • code (text) editor,

Our performance tests focus on “switching between modes” because it has the biggest impact on the smoothness of the user experience due to the necessity of showing the code editor in Edit mode and rendering code on the canvas in View mode by leveraging the algorithms for static syntax highlighting.

Methodology of measurement

Both switching to View mode and Edit mode mainly depend on how many characters, colorized words, and lines of code are presented in the code block widget. In the Chrome DevTools Performance, we made 5 measurements and took an average result for widgets of different sizes in these two tests-cases:

  1. Switching to the Edit mode. It contains all operations that happen from the double click on the widget till the caret appears in the editor: hide text rendered on the canvas, load the Ace editor, set the initial content, and set the cursor on the point of the click.
  2. Switching to the View mode. It contains all operations that happen from clicking outside the widget till the highlighted code is rendered on the canvas: process the code with highlighting service and render on the canvas.

For sample data, we used five widgets of 100, 500, 1000, 2000, and 6000 characters where each character was colorized. Our tests were limited by this value because we had a limitation for storing text-based widgets in 6000 characters. This was an edge case that was very unlikely in reality but had a maximum load on the performance, which was very suitable for testing.

System:

  • MacBook Pro (16-inch, 2019)
  • 2,6 GHz 6-Core Intel Core i7
  • 16 GB 2667 MHz DDR4
  • AMD Radeon Pro 5300 M 4GB
  • Intel UHD Graphics 630 1536 MB

Evaluating the results

We can observe that both entering to Edit and View modes have linear graphs. Switching to View mode is comparatively fast but entering the Edit mode is less performant because it requires starting the editor instance, setting the initial content, and building and rendering a huge tree of HTML elements (for lines, words, line numbers, etc.). The Miro widgets’ nature: flexible form and absence of the scroll bars means that the browser needs to render more HTML elements compared to the code editor which is limited by the screen/window size and the rest of the content is hidden with scroll bars.

Approximately 80% of all code blocks on our boards have less than 1000 characters, so in 80% of cases switching to Edit mode takes under 200ms, which is considered almost unnoticeable for the user. In other cases, users may notice the delay but switching takes less than 1s and it’s still considered acceptable.

Current code block performance meets our requirements but a few bottlenecks have been identified that we can address in our upcoming releases. Although switching between modes depends on the content length, code editing itself works well even for texts exceeding the current limit of 6000 characters.

Final thoughts

Code editing on the web is a fascinating and bottomless topic: how code is parsed and stored, how highlighting is applied, how indentations and tab sizes are calculated, and how it is all rendered. Integrating the code editor on Canvas is connected with even more technical challenges and requires some ingenuity.

There is no universal solution for building code blocks on the canvas. Depending on the whiteboard architecture, users, and tasks they are trying to solve, different options could be used. It’s very easy to encounter performance issues, so code blocks should be built with performance in mind.

By using Ace editor under the hood, with the right adjustments, we crafted a competitive solution that provides rich editing functionality, smooth user experience, and all the agility required for canvas and fuzzy work.

What’s next?

The code block is a very nice feature and there are a lot of ways to make it even more powerful: improve performance, increase the 6k limit, add more languages, implement error highlighting and code suggestions, and add collaboration and code execution. Additionally, we can unblock some new use cases by various integrations, like GitHub, or by exposing the API to our SDK, so users can create their own applications based on the code block.

Let us know in the comments what your approach is for building the code block or what feature you are waiting for the most to be integrated into the code block.

--

--