「君の書いたアニメーション、なんでこんなにガタつくんだ?」
現場でそんな厳しい言葉を投げかけられたことはないかな。あるいは、複雑なJavaScriptを回している最中にスクロールがカクついて、ユーザー体験が台無しになる瞬間を。
フロントエンド・エンジニアとして中級の壁を超えるには、DOMを操作したりAPIを叩いたりするスキルの先に、「ブラウザという巨大なソフトウェアが、どうやってピクセルを画面に叩き出しているか」というアーキテクチャの理解が不可欠だ。
今日は、レンダリング・パイプラインの守護神、「コンポジタースレッド(Compositor Thread)」の正体について話をしよう。こいつを味方に付けられるかどうかで、君の作るプロダクトの「手触り」は劇的に変わる。
—
1. メインスレッドという「超過密労働」の現場
ブラウザの裏側で何が起きているか、まずは現状を整理しよう。
僕たちが書くJavaScriptの実行、DOMの構築、スタイルの計算、そしてレイアウト(リフロー)やペイント(リペイント)。これらはすべて「メインスレッド」という一つの場所で行われている。
想像してみてほしい。メインスレッドは、一人で何役もこなす超多忙な舞台監督だ。
- JSの重い計算をこなす
- ユーザーのクリックをさばく
- 要素の大きさを計算する
- 色を塗る
ここで問題が起きる。JSのループが100ms止まったらどうなる? その間、舞台監督は「色の塗り直し」も「配置の変更」もできない。結果として、画面はフリーズし、ユーザーのスクロール操作すら受け付けなくなる。これが「ジャンク(Jank)」の正体だ。
2. コンポジタースレッド:独立した「描画の専門職」
この地獄のような状況を救うために、モダンブラウザ(Chromeなど)にはコンポジタースレッドという独立したスレッドが存在する。
コンポジタースレッドの役割は、メインスレッドから「ページの断片(レイヤー)」を受け取り、それらを組み合わせて(合成して)最終的な画面を生成することだ。
最大の特徴は、「メインスレッドがJSの計算で死んでいようが、コンポジタースレッドは独立して動き続けられる」という点にある。
なぜスクロールは滑らかなのか?
最近のブラウザでは、スクロール処理はコンポジタースレッドが担当している。メインスレッドが重い処理をしていても、すでに描画済みのレイヤーを上下にずらすだけなら、コンポジタースレッドだけで完結できる。だから、JSが固まっていてもスクロールだけは動く、という挙動が可能なんだ。
—
3. 「レイヤー」という魔法の概念
コンポジタースレッドが効率よく動くために、ブラウザはページをいくつかの「レイヤー(Graphics Layers)」に分割する。
Photoshopのレイヤーを思い浮かべてほしい。背景レイヤーと、動くキャラクターのレイヤーが分かれていれば、キャラクターを動かすときに背景を塗り直す必要はないよね? これと全く同じことがブラウザ内でも起きている。
1. メインスレッドが「どの部分をレイヤーにするか」を決め、描画内容をラスタライズ(ピクセル化)する。
2. その情報をGPUメモリにアップロードする。
3. コンポジタースレッドは、GPUに対して「このレイヤーをこの位置に、この角度で表示しろ」と命令を送るだけ。
この「命令を送るだけ」というのがポイントだ。ピクセルを塗り直す(ペイント)のではなく、既存のビットマップを移動させるだけなので、圧倒的に速い。
—
4. 実戦:コンポジタースレッドを活かすコード、殺すコード
ここで、実務で明日から使える知識を共有しよう。
特定のアニメーションプロパティは「コンポジタースレッドだけで完結する」が、それ以外は「メインスレッドを叩き起こす」必要がある。
避けるべき例:`top` / `left` によるアニメーション
/ ❌ メインスレッドを苦しめる書き方 /
.box {
position: absolute;
top: 0;
transition: top 0.3s;
}
.box:hover {
top: 100px; / レイアウト(リフロー)が発生し、メインスレッドが計算し直す /
}
`top`や`left`を変えると、その要素の周りの配置にも影響が出る可能性がある。ブラウザは「念のためレイアウト全体を再計算しなきゃ!」と判断し、メインスレッドで重い処理を走らせてしまう。
推奨される例:`transform` と `opacity`
/ ✅ コンポジタースレッドに任せる書き方 /
.box {
will-change: transform; / ブラウザに「これ、後で動くからレイヤー化しといて」とヒントを出す /
transition: transform 0.3s;
}
.box:hover {
transform: translateY(100px); / GPUがレイヤーをずらすだけ。メインスレッドは関与しない /
}
`transform`と`opacity`は、他の要素のレイアウトに影響を与えないことが保証されている。そのため、コンポジタースレッドがGPUと直接連携して処理できる。これが「ヌルヌル動く」アニメーションの鉄則だ。
—
5. 現場で使える「レイヤー隔離」のサンプル
では、実際にコンポジタースレッドの恩恵を最大化するためのコード例を見てみよう。
このコードで何が起きるか?
「メインスレッドを3秒フリーズさせる」ボタンを押してみてほしい。
普通のJS処理なら、画面上のすべてが固まるはずだ。しかし、`transform`でアニメーションさせている青いボールは、JSがフリーズしている間も止まることなく滑らかに動き続ける。
これが、コンポジタースレッドの力だ。
---
6. シニアからのアドバイス:武器を正しく使うために
最後に、この仕組みを知った君に気をつけてほしいことが二つある。
1. `will-change` を乱用しないこと:
「何でもかんでもレイヤーに分ければ速くなる」というのは間違いだ。レイヤーを作るということは、その分GPUメモリを消費する。メモリが不足すれば、ブラウザは逆に遅くなる。アニメーションが必要な「ここぞ」という要素にだけ使おう。
2. DevToolsの「Layers」パネルを見ること:
Chrome DevToolsの「三点リーダー」→「More tools」→「Layers」を見てごらん。自分のサイトがどうレイヤー分割されているか視覚的に確認できる。意図しない巨大なレイヤーが作られていないかチェックする癖をつけよう。
ブラウザの仕組みを理解してコードを書くことは、単に「動くものを作る」こととは次元が違う。それは、ユーザーが感じる「心地よさ」の設計そのものなんだ。
次は、ラスタライズの並列化についても話したいが……今日はこの辺にしておこう。まずはこのコンポジタースレッドの仕組みを、自分のプロジェクトで試してみてくれ。健闘を祈る!

コメント