Layersのエンジニアリングチームは、手動で配置された103,802個のドットのファイルサイズを、Illustratorのエクスポートによる膨大な175 MBからわずか82 KBまで削減することに成功しました。この最適化は、複雑でインタラクティブなイラストレーションを、カクつきや遅延なしに低スペックのデバイスでもスムーズにレンダリングできるようにするために必要でした。

マーチャンダイジングやカテゴリーアイコンを含む、手動配置されたドットからレンダリングされた13種類の異なるイラストのコレクション。
手動配置されたドットからレンダリングされた13のLayersイラスト。さまざまな製品マークやカテゴリーアイコンを展示している。layers.engineering

この画像は、Layersのインターフェースで使用されている手動配置ドットの最終的な出力を示している。

IllustratorのSVGエクスポート、Float32エクスポート、未圧縮のDOT2、および圧縮されたDOT2のファイルサイズを比較した対数スケールの棒グラフ。
異なるエクスポート形式のファイルサイズを比較した対数スケールの棒グラフ。175 MBから82 KBへの削減を示している。layers.engineering

このチャートは、エンジニアリングチームによって達成されたジオメトリ配信の大幅な削減を視覚化している。

データ冗長性の特定

パターンデザイナーのAlberto Larizzaによって作成された元の作品は、10万以上のドットで構成されていました。これらをSVGとしてエクスポートすると、これらのドットは円、楕円、パスの混合として表現され、結果として175 MBものソースデータが生じていました。

データの分析により、重要な構造的パターンが明らかになりました。アーティストがパターンを行ごとに構築したため、78%から99%のドットが全く同じY座標を共有していたのです。対照的に、X座標は高エントロピーのままであり、ほぼすべての値が小数でした。

マーチャンダイジングのイラストの切り抜き。行内のドットを繋ぐかすかな線と、yデルタがゼロであることを示すデータストリップが表示されている。
ドットデータの視覚的分析。アーティストが行単位のワークフローに従ったため、yデルタの90%がゼロであることを示している。layers.engineering

この視覚資料は、ドットが同じY座標を共有するデータの構造的パターンを説明している。

「実際にはプレーン化が大部分(半分以上)を担いました。一度それを独自のストリームにすれば、Brotliは連続する何千もの繰り返される行の値を見つけることができます。」

— Shipping 103,802 Dots in 82 KB

DOT2バイナリ形式

データを縮小するために、チームはカスタムバイナリ形式である「DOT2」を開発しました。このプロセスには、座標をuint16に量子化し、半径をuint8にスケーリングし、値を前のドットからの差分(デルタ)として保存することが含まれます。重要なのは、データがインターリーブ(交互配置)ではなく、「プレーン」——すべてのXデルタ、次にすべてのYデルタ、最後にすべての半径という順序——で保存される点です。

このプレーン構造により、Brotli圧縮アルゴリズムは単一のストリーム内で何千もの繰り返される行の値を認識できるようになります。デルタによる削減はわずかな利得でしたが、プレーン構造による組織化は総サイズ削減の半分以上を占め、最終的な圧縮サイズを82 KBまで抑えました。

マーチャンダイジングのイラストのDOT2レイアウトを示す図。ヘッダー、xデルタ、yデルタ、半径に分解されている。
DOT2のレイアウト構造。Brotli圧縮の効率を最大化するためにデータがどのようにプレーンに整理されているかを示している。layers.engineering

この図は、カスタムバイナリ形式のプレーン構造を例証している。

イラストのサイズ削減ステップ: Float32 ソース 1.25 MB; Float32 + Brotli 224 KB; Quantized interleaved 227 KB; Quantized sorted interleaved 240 KB; Quantized sorted planar 83 KB; DOT2 82 KB
イラストのサイズ削減ステップlayers.engineering

HN Digestによる図表(layers.engineeringの報告数値に基づく。数値はソースの表記通り)。

この形式はロスのあるものですが、チームは最大座標誤差が0.011px未満、半径誤差が0.0065px未満であることを確認しました。これはユーザーには感知できないレベルです。

ランタイムとレンダリングの最適化

ワイヤ形式の削減に加え、チームはブラウザがドットを処理する方法も最適化しました。彼らは、各ドットが11個のフィールドを持つJavaScriptオブジェクトであったオブジェクトベースのシステムから、10個のフラットな型付き配列(Float64Array)へと移行しました。これにより、スプリング物理演算の挙動を元のプロトタイプとビット単位で同一に保つことができます。

チームはまた、背景グリッドのレンダリングを刷新しました。古いループは毎フレームグリッドのすべてのセルを訪問していましたが、新しいループはアクティブなセルとユーザーのカーソルの特定のバウンディングボックスのみを訪問します。この変更により、グリッドがアイドル状態のときにはページがアニメーションフレームの要求を完全に停止できるようになりました。