SVG だけで作るノイズ & フリーフォームグラデーション

## この記事の概要

2023 年 8 月 20 日開催のなごデ LT という LT イベントで発表する内容です。

[Grainy Gradient](https://www.google.com/search?q=Grainy+Gradient&tbm=isch)[^title] と調べると、ザラっとした質感でフリーフォームなグラデーションが出てきます。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/0cdd757e-72d6-c21f-26ce-019ba400cb23.png)

こういった表現をデジタルプロダクトで実施しようと思うと、どうしても画像に頼りがちです。
画像で作ると、少しだけ色を変えたいときに毎度画像ごと編集する必要があり手間がかかります。
また、ワンポイントで使うならいざしらず、背景全面に敷こうなどとすれば結構なファイルサイズ[^file-size]になりそうで好ましくありません。

というわけで、サイズの軽い svg で実現してみせようという記事です。

[^title]: 「グレイニーグラデーション」と記載してもあまりにも馴染みがなさそうだったので、タイトルでは「ノイズ & フリーフォームグラデーション」としています。

[^file-size]: 試しに、高解像度ディスプレイでも荒れないくらいのサイズで作ったら jpg ですら 数 MB になってしまいました。更に png では 10 MB を超えました。嫌すぎます。

## 作ったもの

ディスプレイに大きく表示しないと分かりづらいので GitHub Pages で公開しました。

https://xrxoxcxox.github.io/qiita-grainy-gradient/

背景画像だけを抜き出すとこんな感じです。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/f7ec6206-4b0b-8242-9783-119244e87062.png)

色の違いが分かりやすいようなグラフィックにしたつもりですが、実際は様々な色や形状で作れます。

## 作り方

### コードの全体像

```xml
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg"> <!-- 1. 好きな viewBox のサイズ -->
  <g filter="url(#noise)"> <!-- 3. filter の用意 -->
    <!-- 2. 色々なパス -->
  </g>
  <defs>
    <filter id="noise"> <!-- 3. filter の用意 -->
      <feTurbulence baseFrequency="1" numOctaves="1" stitchTiles="stitch" type="fractalNoise" result="turbulence"/> <!-- 3.1. feTurbulence -->
      <feGaussianBlur in="SourceGraphic" stdDeviation="64 8" result="blur"/> <!-- 3.2. feGaussianBlur -->
      <feBlend mode="color-burn" in="turbulence" in2="blur"/> <!-- 3.3. feBlend -->
    </filter>
  </defs>
</svg>
```

### 1. 好きな viewBox のサイズ

見本では `viewBox="0 0 1024 1024"` としていますが、基本的にどんなサイズでも構いません。
ただし、実際に表示する際の幅や高さとあまりにもかけ離れたサイズを指定すると、綺麗なノイズが出ないなどの問題が起き得ます。

大きければ大きいほど良い、というものでもないので、ほどほどのサイズで指定しておきましょう。

```xml
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
</svg>
```

### 2. 色々なパス

この領域に自分の好きな色・形のパスを用意します。
今回はこんなものを用意しました。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/4281a067-a669-83ef-ede6-b16e7264d6f7.png)

正直言うと、どうしても後から色々微調整する必要はあるので、この段階で神経質になる必要はありません。

ここまでのコードは次のようになります。

```diff_xml
  <svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
+   <g>
+     <!-- 色々なパス -->
+   </g>
  </svg>
```

### 3. filter の用意

先ほど作ったパスにノイズやぼかしを加えるためには `filter` を使います。

今回は 1 つしか用意していませんが、複数のフィルターを用意して、それぞれのパスで `filter="url(#noise1)"`, `filter="url(#noise2)"`, `filter="url(#noise3)"` のように適用することも可能です。

ここまでのコードは次のようになります。

```diff_xml
  <svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
-   <g>
+   <g filter="url(#noise)">
      <!-- 色々なパス -->
    </g>
+   <defs>
+     <filter id="noise">
+     </filter>
+   </defs>
  </svg>
```

#### 3.1 feTurbulence

フラクタルノイズやタービュレントノイズを作れます。
色々な設定ができますが、次のような内容をおすすめします。

- baseFrequency
    - 0.8 - 1.2 程度
- numOctaves
    - 1 - 3 程度
- stitchTiles
    - stitch
- type
    - fractalNoise

組み合わせ方によって大きく見た目が変わるので、詳細が気になる人は MDN Docs などをどうぞ。

https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feTurbulence

ここまでのコードは次のようになります。
（`feTurbulence` の中に `result` という属性があり、ここまでノータッチですが、後から説明します。）

```diff_xml
  <svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
    <g filter="url(#noise)">
      <!-- 色々なパス -->
    </g>
    <defs>
      <filter id="noise">
+       <feTurbulence baseFrequency="1" numOctaves="1" stitchTiles="stitch" type="fractalNoise" result="turbulence"/>
      </filter>
    </defs>
  </svg>
```

現段階での見た目は次のようになります。
……先ほど作成したパスがすべて消えて、あたり一面のノイズになってしまいましたが、大丈夫です。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/5d61e7de-b246-be7f-9ea3-9b16733ef2fc.png)

#### 3.2 feGaussianBlur

ガウスぼかしをかけられます。

`in="SourceGraphic"` を指定した場合、元々の画像をぼかす、と考えてもらえれば大丈夫です。

`stdDeviation` はぼかし半径です。
ただ、水平ぼかし量と垂直ぼかし量をそれぞれ設定できます。
別の値を設定しておいた方がより表情豊かな見た目になると思います。

今回は `stdDeviation="64 8"` と設定しました。

こちらも詳細が気になる人は MDN Docs などをどうぞ。

https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feGaussianBlur

ここまでのコードは次のようになります。

```diff_xml
  <svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
    <g filter="url(#noise)">
      <!-- 色々なパス -->
    </g>
    <defs>
      <filter id="noise">
        <feTurbulence baseFrequency="1" numOctaves="1" stitchTiles="stitch" type="fractalNoise" result="turbulence"/>
+       <feGaussianBlur in="SourceGraphic" stdDeviation="64 8" result="blur"/>
      </filter>
    </defs>
  </svg>
```

現段階での見た目は次のようになります。
ぼかしがかかった代わりにノイズが完全に消滅しましたが、これも現段階では大丈夫です。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/6ad9e3e1-1d3c-8cfb-9ea7-c3350c7acbad.png)

#### 3.3 feBlend

ここまで、ノイズだけになったりぼかしだけになったりで、狙った通りの見た目になりませんでした。
今回の `feBlend` を使うことで解消します。

見本のコードで書かれるだけ書かれていて触れられていなかった `result` とともに解説します。

`feBlend` には `in` と `in2` という属性があり、この 2 つに渡した内容をブレンドします。
ブレンドの方法は [CSS の `mix-blend-mode` と同じ](https://developer.mozilla.org/en-US/docs/Web/CSS/blend-mode)です。
今回は `color-burn` を選びましたが、`overlay` でも `hard-light` でもお好きなものをどうぞ。

こちらも詳細が気になる人は MDN Docs などをどうぞ。

https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feBlend

ここまでのコードは次のようになります。

```diff_xml
  <svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
    <g filter="url(#noise)">
      <!-- 色々なパス -->
    </g>
    <defs>
      <filter id="noise">
        <feTurbulence baseFrequency="1" numOctaves="1" stitchTiles="stitch" type="fractalNoise" result="turbulence"/>
        <feGaussianBlur in="SourceGraphic" stdDeviation="64 8" result="blur"/>
+       <feBlend mode="color-burn" in="turbulence" in2="blur"/>
      </filter>
    </defs>
  </svg>
```

これで、最初に示したのと同じ見た目になりました。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/f7ec6206-4b0b-8242-9783-119244e87062.png)

## 最後に

見本では簡単めなコードにしましたが、複数の `feBlend` を組み合わせて更に複雑な表情を作ることも可能です。

ちなみに、似たような内容で以前こんな記事も書きました。
こちらもあわせて読んでいただけたら嬉しいです。

https://qiita.com/xrxoxcxox/items/2c95837c134ead32f591

---

最後まで読んでくださってありがとうございます！
[X (Twitter)](https://twitter.com/xrxoxcxox)でも情報を発信しているので、良かったらフォローお願いします！

[Devトーク](https://jobs.qiita.com/employers/qiita-inc/dev_talks/489)でお話してくださる方も募集中です！