CSSのimage-renderingでドット絵をぼかさず拡大する方法
小さなドット絵を <img> で大きく表示すると、ブラウザが色をなめらかにつないで拡大するので、ドットの境目がぼけます。拡大する要素に image-rendering: pixelated を指定すると、1つのドットがそのまま大きな四角になり、元の色だけで描かれます。値ごとの違い、ドットの幅がそろわなくなる大きさ、背景画像や <canvas> に使う場合も確かめました。
検証環境は Ubuntu 24.04、Node.js 22.23.4、Playwright 1.63.0、Chromium 153 です。8×8 ピクセルのドット絵(4色)を作り、拡大して表示したページを Playwright で撮って、画像に使われている色の数と、色が変わる位置を数えました。Firefox と Safari、スマートフォンの実機は試していません。ほかのブラウザの対応状況は、MDN のブラウザー互換性データ(@mdn/browser-compat-data 8.1.8)の値です。
image-rendering: pixelated を付ける
CSS は次の1行です。
/* ドット絵を拡大してもぼかさない */
.pixel-art {
image-rendering: pixelated;
}
拡大して表示する画像に、このクラスを付けます。ここでは 8×8 ピクセルの画像を、10倍の 80px で表示しています。
<img class="pixel-art" src="cat.png" width="80" height="80" alt="">

image-rendering は、画像を元の大きさと違う大きさで表示するときの、拡大・縮小のしかたを指定するプロパティです。初期値の auto では、どう拡大するかはブラウザに任されます。
検証環境の Chromium で 80px に拡大した画像を撮ったところ、auto では色の数が585色になりました。元の画像は白・黒・水色・ピンクの4色なので、境目に中間の色が作られています。目の高さの1行を左から見ると、黒から水色に変わるまでの間に、少しずつ色が違う中間の色の画素が9画素並んでいました。pixelated にすると色の数は4色で、どの画素も、元の1ドットを10×10pxにした色と一致しました。
crisp-edges との違いと、Chrome で smooth が使えないこと
ぼかさない値には、pixelated のほかに crisp-edges もあります。どちらも、元の色を混ぜずに拡大するための値です。仕様の CSS Images Module Level 3 では、2つは次のように分けて書かれています。
| 値 | 仕様に書かれている拡大のしかた |
|---|---|
pixelated |
縦と横それぞれで、元の大きさの整数倍のうち目標の大きさにいちばん近いものまで最近傍法(nearest neighbor)で拡大し、残りの差を smooth と同じようになめらかに調整する |
crisp-edges |
境目をぼかさず、色を混ぜない方法で拡大する。最近傍法のほか、斜めの線を検出してなめらかに描くような方法もブラウザが選べる |
最近傍法は、仕様の説明では、拡大した先の画素それぞれについて、その画素の範囲の中から1点を選び、元の画像でその位置にある色をそのまま使う方法です。仕様には、整数倍の大きさなら pixelated は crisp-edges と同じ結果になる、とも書かれています。検証環境でも、10倍に拡大した crisp-edges の画像は4色で、pixelated と同じく、どの画素も元のドットの色と一致しました。
もう1つの値の smooth は、写真のように、なめらかに拡大したい画像のための値です。ただ、検証環境の Chromium では、image-rendering: smooth と書いても getComputedStyle() で読んだ値は auto のままでした。MDN のブラウザー互換性データでも、smooth に対応しているのは Firefox だけです。
対応状況は次のとおりです(MDN のブラウザー互換性データより。数字は対応したバージョン)。
| 値 | Chrome | Edge | Firefox | Safari |
|---|---|---|---|---|
pixelated |
41 | 79 | 93 | 10 |
crisp-edges |
148 | 148 | 65 | 7 |
smooth |
なし | なし | 93 | なし |
crisp-edges は、Chrome と Edge では 148 からの対応です。それより前は -webkit-optimize-contrast という別名で対応していました。pixelated は Chrome と Edge の古いバージョンでも同じ名前で使えるので、ドット絵を拡大する目的なら、こちらが使いやすい値です。
整数倍でない大きさだと、ドットの幅がそろわない
8×8 の画像を 100px(12.5倍)で表示すると、1つのドットの幅は計算上 12.5px になります。仕様には、整数倍でない大きさを最近傍法で拡大すると、元は同じ太さだった線が、場所によって1画素細くなったり太くなったりすることがある、と書かれています。
検証環境で、pixelated を指定して 100px で表示した画像の、目の高さの1行を調べました。色が変わる位置は次のとおりで、ドットの幅は 13px、12px、13px…と交互に変わっていました。
0-12:黒 13-24:水色 25-37:黒 38-62:水色 63-74:黒 75-87:水色 88-99:黒
38〜62 の水色は、水色のドットが2つ続いた部分で、合わせて 25px です。この大きさでも、画像の色は4色のままでした。仕様にある「整数倍まで拡大してから、なめらかに調整する」動きは、検証環境の Chromium では見られず、pixelated と crisp-edges の画像は、全部の画素が同じ色でした。
ドットの幅をそろえたいときは、元の大きさの整数倍で表示します。ここで数えるのは CSS の px ではなく、画面の実際の画素です。Playwright で deviceScaleFactor を 2 にして、1px を2画素で描く画面にしたところ、同じ 100px の表示が 200 画素(25倍)になり、どの画素も元のドットの色と一致しました。
背景画像や、親要素への指定にも効く
image-rendering は、<img> だけでなく、要素の背景画像にも効きます。仕様では、要素の背景画像や、リストの行頭に表示する画像(list-style-image)など、プロパティで指定したすべての画像に効くと書かれています。
.bg {
width: 80px;
height: 80px;
background: url(cat.png) no-repeat 0 0 / 80px 80px;
}
<div class="bg pixel-art"></div>
検証環境では、background-size で 80px に拡大した背景画像も、pixelated を指定すると4色で、元のドットと一致しました。指定しないときは、<img> と同じく585色になりました。
image-rendering は子の要素に継承されるプロパティです。親の要素に image-rendering: pixelated を指定し、子の <img> には何も書かなかったところ、子の getComputedStyle() の値は pixelated で、画像もぼけずに表示されました。ドット絵をまとめて並べる場所があるなら、その親に1回書くだけで足ります。
canvas でドット絵を拡大するとき
<canvas> でドット絵を扱うときは、拡大のしかたが2通りあり、それぞれ指定するものが違います。
1つめは、8×8 の小さな canvas に描いて、CSS の width と height で 80px に広げる方法です。これは <img> と同じで、canvas 要素に image-rendering: pixelated を指定すると、4色のまま拡大されました。指定しないと、585色にぼけました。
2つめは、80×80 の canvas に drawImage() で10倍に描く方法です。この場合は、CSS ではなく、canvas の imageSmoothingEnabled を false にします。既定値は true で、拡大した画像がなめらかにつながれます。
// canvas(80×80)と、読み込み済みの img(8×8)は用意してある
const ctx = canvas.getContext('2d');
ctx.imageSmoothingEnabled = false;
ctx.drawImage(img, 0, 0, 80, 80);
deviceScaleFactor が 1 の画面では、これだけで4色になり、元のドットと一致しました。ところが、deviceScaleFactor を 2 にすると、同じ canvas が31色になりました。canvas の中身は 80×80 画素で描かれていて、画面では 160×160 画素に広げて表示されます。HTML の仕様では、imageSmoothingEnabled は drawImage() とパターンの塗りで画像をなめらかにするかどうかの設定です。この広げる段階のぼけは、imageSmoothingEnabled では直せませんでした。

canvas 要素にも image-rendering: pixelated を指定したところ、deviceScaleFactor が 2 の画面でも4色になり、元のドットと一致しました。drawImage() で拡大するときは、imageSmoothingEnabled = false と、canvas 要素の image-rendering: pixelated の両方を指定します。





