042刊行 2026-10-20 ・ 読了約6分

ブラウザは、ひとりで作れるのか。

Can One Person Build a Web Browser?

Python 1,859行で、ウィキペディアの記事まで描けた。ところが自作の20問で本物と全問一致していたのに、ブラウザ各社の共通試験では1.2%しか解けなかった。比べていたのは body の中だけだった。

調べ方Python で自作した描画エンジンと、web-platform-tests の木構築1,739問

規模
Python 1,859行・試験1,739問
触れる
段階を選ぶと、同じページの途中経過が出る
結論
描ける。試験は42%。
Abstract

要旨

Chrome も Safari も、何百人もの技術者が何十年もかけて作ってきた。ブラウザは、ひとりで作れるのか。

仕組みを知るために、Python で描画エンジンを自作した。URL を渡すと、文字列を読み、木を組み、CSS を当て、箱を並べ、絵にする。途中の7段階を全部ファイルに書き出す作りにした。コードは7つのファイルで1,859行、借りたのは画像を書き出す Pillow だけである。

描くところまでは、作れた。手元の見本ページは1,265文字から289の塗る命令を経て1枚の絵になり、日本語版ウィキペディアの記事も6,405個のノードと1,011本の CSS 規則を読んで描けた。

おもしろかったのは、その先である。壊れた HTML の直し方を自分で20通り作り、Chrome と Safari のエンジンに並べて比べると、20通りすべてで同じ木になった。ところが、ブラウザ各社が使う共通の試験問題1,739問を解かせると、正解は21問、1.2%だった。

自分で作った検査は、自分の思い込みしか検査しない。

Method

方法

作り
取得・分解・木・スタイル・レイアウト・描画命令・画素の7段階を、1つずつ別のファイルに分けて書いた。それぞれの段が、次の段に渡すものを人が読める形で書き出す。本物のブラウザの内側で起きていることを、番号順に開いて追えるようにするためである。
見本
閉じ忘れの p や li、CSS の @media や :hover をわざと混ぜた試験ページ(1,265文字)と、example.com、日本語版ウィキペディアの「ウェブブラウザ」の記事。
答え合わせ1
壊れた HTML を自分で20通り作り、自作・Chrome(エンジンは Blink)・Safari と同じ WebKit の3つに読ませて、組み上がった木を並べた。最初の10通りは直しながら見た問題、あとの10通りは直したあとに足した、まだ見ていない問題である。
答え合わせ2
ブラウザ各社の共通試験 web-platform-tests にある、HTML の木の組み立ての問題。全1,959問のうち、入口の違う部分解析(206問)と JavaScript が前提の問題(14問)を除いた1,739問を採点した。木を決まった書式で書き出し、1文字でも違えば不正解とした。
Result 1

文字列は、7回姿を変えて絵になる

見本ページを幅640で描かせると、7つの段はそれぞれ次のものを書き出した。

87個
文字列を切ったトークン
73個
木のノード(補修 13 か所)
289個
塗る命令

いちばん手間がかかるのは、文字を行に折る段だった。英語は空白で折れるが、日本語は1文字ずつ折れる。そのうえ「。」や「」」は行の頭に来てはいけないので、前の字にくっつけて離さない(禁則)。見本ページ1枚の各段の所要時間は、レイアウトが10.7ミリ秒、画素にする段が67.5ミリ秒で、ほかの段は2ミリ秒に届かなかった。

「見出しが大きい」「リンクが青い」も、HTML そのものの性質ではなかった。ブラウザは最初に、自前の既定の CSS を当てている。自作では94本の規則を書き、その上にページの規則を重ねた。

h1 が大きいのは、ブラウザが先にそう書いておいたからだった。

Result 2

自作の20問は、全問一致した

閉じ忘れや入れ子の交差のような壊れた HTML を読ませると、Chrome と WebKit は、最初の10通りすべてで同じ木を組んだ。別の会社のエンジンでも、直し方が仕様で1手ずつ決まっているからである。

最初の自作は、そのうち9通りで本物と違った。合っていたのは、li の閉じ忘れだけだった。そこで、HTML の仕様にある木の組み立ての手順を写していった。

範囲
閉じタグは、表やセルの「壁」の外にある要素を閉じない。
書式の作り直し
開いた b や i を覚えておき、途中で閉じられても、次の文字の前に作り直す。
養子縁組
<b>太<p>段落</b> のように入れ子が交差したら、書式の要素を複製して付け直す。仕様での正式な名前も adoption agency(養子縁組の手続き)である。
表からの追い出し
表の骨組みに置けない文字は、表の直前へ移す。<table> の中に迷い込んだ「迷子」という文字は、表の前に出てくる。

直したあとは、最初の10通りも、新しく足したまだ見ていない10通りも、20通りすべてで3者が一致した。まだ見ていない問題でも合ったので、仕様を写せたと考えた。

Result 3

公式の試験では、1.2%だった

次に、ブラウザ各社が実際に使っている共通の試験問題を解かせた。結果は1,739問中21問だった。

1.2%
初回(21問)
20.6%
head を補ったあと(358問)
42.2%
注釈と doctype を入れたあと(734問)

原因は、問題の難しさではなかった。自作の比べ方は、body の中身しか見ていなかった。公式の問題は木の全体を見る。<head> を書かない HTML で空の head を補っていなかったのが、ほぼ全問に効いていた。そこを1か所直すだけで、正解は21問から358問に増えた。

続けて、注釈 <!-- --> と doctype を木に入れた。注釈の行き先は、読んだ位置で変わる。<html> の前なら文書の直下、</body> の後なら html の最後の子、それ以外は開いている要素の中である。

途中で一度、40.8%に下がった。「body が始まる前の閉じタグは捨てる」という規則を広く当てすぎ、解けていた問題を壊したためである。当てる範囲を <html> の前だけに絞り、42.2%になった。

20問の全問一致は、20問ぶんの自信でしかなかった。

Case

作らなかったもの

試験の問題を中身で分けると、解けている所と、まったく手が付いていない所がはっきり分かれた(1問が複数の分類に入る)。

よく解ける
文字参照(&amp; など)は137問中120問で87.6%。下のどの特別な要素も含まない問題は188問中136問で72.3%。
半分に届かない
表は273問中92問で33.7%、select は92問中29問で31.5%、注釈を含む問題は430問中118問で27.4%。
0%
HTML の中の SVG と数式(221問)、template(125問)、frameset(110問)、NUL と CR の文字(40問)。どれも、普通の HTML とは別の規則で木を組む。

描画の側にも、わざと作っていないものがある。JavaScript は動かない。position で浮かせる箱は捨て、flex と grid は縦に積むだけにした。画像は中身を描かず「[画像: 説明]」と文字で出す。上下の余白を相殺する規則も無いので、見出しと箇条書きのあいだが本物より広い。

どれも、本物のブラウザの仕様の大半を占める部分である。ひとりで作れたのは、ページを「読んで、描く」ところまでだった。

Try it

段階を切り替える

見本ページ1枚について、7つの段が書き出したものを切り替えて見られる。抜粋は、自作のブラウザが実際に書き出したファイルからそのまま取った。


      
    

幅640で描かせた結果。絵は2倍の細かさで書き出したものを縮めて載せている。抜粋は各段の先頭部分だけで、長い行は枠の中で横に送って読める。

Conclusion

結論

ブラウザは、ページを読んで描くところまでなら、ひとりで作れた。1,859行で、日本語版ウィキペディアの記事が描ける。ただ、本物と同じように読めているかを公式の試験で測ると、42.2%だった。

いちばんの発見は、作ったものより測り方のほうにあった。自分で作った20問は、自分が気にしていた所しか見ていなかった。head を補っていないことは、自分の問題では一度も見つからず、他人が作った1,739問では、ほぼ全問で見つかった。全問一致は、問題を作った人と答えた人が同じだったからだった。

本物のブラウザが巨大なのは、描く仕組みが難しいからだけではない。どんなに壊れたページでも、どの会社のエンジンでも同じ木になるように、直し方を1手ずつ決めた仕様と、それを確かめる何千もの共通問題を積み上げてきたからである。

次の問い:残りの58%のうち、どこまでが「ひとり」で届くのか。

※ 仕組みを知るために作ったおもちゃのブラウザの記録です。
採点したのは HTML の木の組み立てだけで、CSS やレイアウトの正しさは測っていません。数字はすべて、2026年10月2日〜4日に手元で書き出したファイルとログから取りました。1問が複数の分類に入るため、分類ごとの問題数の合計は1,739問になりません。
References

ネタもと