Webstrates A research prototype enabling collaborative editing of websites through DOM manipulations.

While op-throttle seems stable for the moment, op-compose is still very experimental and should only be used on single paragraphs/text nodes (if at all), as it may otherwise create errors, causing DOM inconsistensies across clients.

Users of very active webstrates can sometimes incur poor performance, partly because of the strain put on the server by all the operations generated, and partly because of all the operations the clients themselves have to handle.

To combat this, developers can limit the number of operations being sent to the server with op-throttle in some situations, and in others group and compose operations together to speed up the processing with op-compose.

Throttling operations

Using the op-throttle attribute, a developer can limit the number of operations being generated by certains actions. When the op-throttle attribute is set on an element with an integer value n, only one operation will be allowed to be sent every n-th millisecond. For instance, with the following <div> element, regardless of how many DOM changes are applied to the element, only one operation every 100th milliseconds will be sent to the server.

<div op-throttle="100"></div>

This means that for writing text into an element or appending DOM elements, op-throttle is almost guranteed to eventually cause inconsistencies as some operations will never be sent to other clients! In these use cases, op-compose can be used instead.

However, for operations that are idempotent, op-throttle will work well. Consider this example of a shared cursor:

<html><head>
<style>
.cursor { background: #000; height: 10px; width: 10px; position: absolute; border-radius: 5px; }
</style>
<script>
  webstrate.on('loaded', () => {
    document.addEventListener('mousemove', (event) => {
      document.querySelector('.cursor').setAttribute('style',
        `top:${event.clientY}px;left:${event.clientX}px;`);
    });
  });
</script>
</head>
<body>
  <div class="cursor" style="top:0px;left:0px;"></div>
</body></html>

When moving the cursor around, a little black ball will attach itself to the cursor, making it possible for other clients to follow the user’s cursor on their own screen. Functionality like this can be useful in many collobrative applications, but constantly updating the cursor’s position will generate a lot of operations. In an example like this, moving the cursor around for a second can easily generate 30-50 operations.

By adding op-throttle="100" to the <div> element, however, at most one operation will be sent every 100th millisecond, and the last generated operation is always guaranteed to arrive, so the DOM will eventually reach consistency. The movement of the cursor may appear to behave more choppily depending on networking conditions, but in many scenarios this is a small price to pay for reducing operations sent by up to 80%.

The avid Webstrates developer may realize that even when setting a direct attribute as in the above example, Webstrates will in most cases try to generate diffs and send those rather than the full operation, thus causing even seemingly idempotent operations to become dependent on previously generated operations. As a preventive measure, diffing therefore automatically gets disabled when using op-throttle.

Composing operations

When op-throttle can’t be used, performance gains can still be achived by using op-compose composing operations and sending them in groups. When setting op-compose on a DOM element in the same manner as op-throttle, operations will be sent only once every n-th millisecond.

What it means to group operations is self-explanatory: Instead of sending 10 operations as 10 different packages, all operations get sent in one package. This reduces network overhead, combined serialization and deserialization time, as well as processing time.

However, op-compose takes it one step further and also tries to compose many operations into fewer. For instance, if a user writes “Hello” into a contenteditable text field, this will usually result in 5 operations, one for each letter. When using op-compose, the Webstrates client code will combine these 5 operations into a single operation, reducing the package size and significantly speeding up the processing time.