グーフィルター(粘性融合)

feGaussianBlurとfeColorMatrixで要素同士が溶け合うメタボール表現。ローダーやメニュー展開の演出に最適です。

#svg#filter#css#animation

ライブデモ

使用例(お題: アイドルグループ Sakura)

この技法を「アイドルグループ Sakura」というテーマのダミーサイトで実際に使った例です。

HTML
<!-- Sakura:ライブ告知ヒーロー。ペンライトの光が溶け合うグーフィルター -->
<section class="sk-live">
  <!-- グーフィルター定義(不可視) -->
  <svg class="sk-defs" aria-hidden="true" width="0" height="0">
    <defs>
      <filter id="skGoo">
        <feGaussianBlur in="SourceGraphic" stdDeviation="9" result="blur" />
        <feColorMatrix in="blur" mode="matrix"
          values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 20 -9" result="goo" />
        <feBlend in="SourceGraphic" in2="goo" />
      </filter>
    </defs>
  </svg>

  <!-- 主役:周回する光がメタボール状に融合 -->
  <div class="sk-blobs" aria-hidden="true">
    <span class="sk-blob sk-core"></span>
    <span class="sk-blob sk-b0"></span>
    <span class="sk-blob sk-b1"></span>
    <span class="sk-blob sk-b2"></span>
    <span class="sk-blob sk-b3"></span>
  </div>

  <div class="sk-live__copy">
    <span class="sk-live__tag">SAKURA SPRING LIVE 2026</span>
    <h1 class="sk-live__title">満開のステージで、<br>会いに行くよ。</h1>
    <p class="sk-live__date">4.18 SAT / 4.19 SUN @さくらアリーナ</p>
    <a class="sk-live__btn" href="#">チケット先行抽選に応募</a>
  </div>
</section>
CSS
/* Sakura:ライブ告知ヒーロー(グーフィルターのペンライト融合) */
:root {
  --pink: #ffd1e0;
  --pink-deep: #ff6fa3;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  height: 400px;
  font-family: "Hiragino Kaku Gothic ProN", "Yu Gothic", system-ui, sans-serif;
  overflow: hidden;
}

.sk-live {
  position: relative;
  height: 400px;
  display: grid;
  place-items: center;
  text-align: center;
  color: #fff;
  background:
    radial-gradient(110% 90% at 50% 110%, #ff9ec4 0%, #d96aa0 38%, #6e3a63 100%);
}
.sk-defs { position: absolute; }

/* 融合エリア。filterは親に1回だけ */
.sk-blobs {
  position: absolute;
  inset: 0;
  filter: url(#skGoo);
  opacity: 0.9;
}
.sk-blob {
  position: absolute;
  top: 50%;
  left: 50%;
  border-radius: 50%;
  /* ペンライトの光:中心が白く外がピンク */
  background-image: radial-gradient(circle at 35% 30%, #ffffff, var(--pink) 45%, var(--pink-deep) 100%);
}
.sk-core { width: 120px; height: 120px; margin: -60px 0 0 -60px; }
.sk-b0 { width: 60px; height: 60px; margin: -30px 0 0 -30px; animation: skOrbit 5s linear infinite; }
.sk-b1 { width: 48px; height: 48px; margin: -24px 0 0 -24px; animation: skOrbit 6.5s linear infinite reverse; }
.sk-b2 { width: 70px; height: 70px; margin: -35px 0 0 -35px; animation: skOrbit2 5.6s linear infinite; }
.sk-b3 { width: 40px; height: 40px; margin: -20px 0 0 -20px; animation: skOrbit2 7.4s linear infinite reverse; }

@keyframes skOrbit {
  from { transform: rotate(0deg) translateX(110px) rotate(0deg); }
  to   { transform: rotate(360deg) translateX(110px) rotate(-360deg); }
}
@keyframes skOrbit2 {
  from { transform: rotate(0deg) translateX(150px) rotate(0deg); }
  to   { transform: rotate(360deg) translateX(150px) rotate(-360deg); }
}

/* コピーは融合の上に重ねる */
.sk-live__copy { position: relative; z-index: 2; padding: 0 22px; }
.sk-live__tag {
  display: inline-block;
  font-size: 10px;
  letter-spacing: 0.28em;
  font-weight: 700;
  padding: 5px 14px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.22);
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
}
.sk-live__title {
  margin: 16px 0 12px;
  font-size: 28px;
  line-height: 1.45;
  font-weight: 800;
  text-shadow: 0 4px 16px rgba(110, 58, 99, 0.6);
}
.sk-live__date {
  margin: 0 0 20px;
  font-size: 13px;
  letter-spacing: 0.04em;
  color: rgba(255, 255, 255, 0.92);
}
.sk-live__btn {
  display: inline-block;
  padding: 12px 26px;
  border-radius: 999px;
  background: #fff;
  color: var(--pink-deep);
  font-size: 13px;
  font-weight: 800;
  text-decoration: none;
  box-shadow: 0 10px 26px rgba(255, 111, 163, 0.45);
  transition: transform 0.2s;
}
.sk-live__btn:hover { transform: translateY(-2px); }

@media (prefers-reduced-motion: reduce) {
  /* 静止時は少しずらして固める */
  .sk-blob { animation: none !important; }
  .sk-b0 { transform: translateX(90px); }
  .sk-b1 { transform: translate(-80px, 40px); }
  .sk-b2 { transform: translate(70px, -70px); }
  .sk-b3 { transform: translate(-60px, -60px); }
  .sk-live__btn { transition: none; }
}
JavaScript
// 中央コアをゆっくり脈動させ、ペンライトの「光のかたまり」感を強める
const core = document.querySelector(".sk-core");

if (core) {
  const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;

  if (!reduce) {
    let t = 0;
    const pulse = () => {
      t += 0.03;
      // 0.92〜1.08 の範囲でゆっくり拡縮
      const s = 1 + Math.sin(t) * 0.08;
      core.style.transform = `translate(-50%, -50%) scale(${s.toFixed(3)})`;
      requestAnimationFrame(pulse);
    };
    // marginで中央化済みなので、transform原点を中心へ揃えて拡縮
    core.style.margin = "0";
    core.style.transformOrigin = "center";
    requestAnimationFrame(pulse);
  }
}

コード

HTML
<!-- SVGグーフィルター: blur+colormatrixで要素が粘性をもって融合する -->
<div class="goo-stage">
  <!-- フィルター定義(不可視) -->
  <svg class="defs" aria-hidden="true" width="0" height="0">
    <defs>
      <filter id="goo">
        <!-- ぼかして -->
        <feGaussianBlur in="SourceGraphic" stdDeviation="9" result="blur" />
        <!-- アルファのコントラストを強めて輪郭をくっつける -->
        <feColorMatrix in="blur" mode="matrix"
          values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 20 -9" result="goo" />
        <feBlend in="SourceGraphic" in2="goo" />
      </filter>
    </defs>
  </svg>

  <!-- このコンテナにフィルターをかけ、中の円が融合する -->
  <div class="blobs">
    <span class="blob b0"></span>
    <span class="blob b1"></span>
    <span class="blob b2"></span>
    <span class="blob b3"></span>
    <span class="blob core"></span>
  </div>
  <p class="caption">gooey filter — メタボール風の融合</p>
</div>
CSS
* { box-sizing: border-box; }
body {
  margin: 0;
  min-height: 100vh;
  display: grid;
  place-items: center;
  font-family: "Segoe UI", system-ui, sans-serif;
  background: radial-gradient(120% 120% at 50% -10%, #312e81 0%, #1e1b4b 45%, #0a0a1f 100%);
  overflow: hidden;
}
.goo-stage {
  display: grid;
  justify-items: center;
  gap: 18px;
}
.defs { position: absolute; }

/* 融合エリア。filterは親に1回だけかける */
.blobs {
  position: relative;
  width: 260px;
  height: 220px;
  filter: url(#goo);
}
.blob {
  position: absolute;
  border-radius: 50%;
  background: #f472b6;
  /* グラデで艶を出す */
  background-image: radial-gradient(circle at 35% 30%, #fbcfe8, #f472b6 45%, #c026d3 100%);
  top: 50%;
  left: 50%;
}
/* 中央のコア */
.core {
  width: 90px; height: 90px;
  margin: -45px 0 0 -45px;
}
/* 周回する4つの粒。サイズと半径・速度をずらす */
.b0 { width: 46px; height: 46px; margin: -23px 0 0 -23px; animation: orbit 4.5s linear infinite; }
.b1 { width: 38px; height: 38px; margin: -19px 0 0 -19px; animation: orbit 6s linear infinite reverse; }
.b2 { width: 54px; height: 54px; margin: -27px 0 0 -27px; animation: orbit2 5.2s linear infinite; }
.b3 { width: 30px; height: 30px; margin: -15px 0 0 -15px; animation: orbit2 7s linear infinite reverse; }

@keyframes orbit {
  from { transform: rotate(0deg) translateX(70px) rotate(0deg); }
  to   { transform: rotate(360deg) translateX(70px) rotate(-360deg); }
}
@keyframes orbit2 {
  from { transform: rotate(0deg) translateX(95px) rotate(0deg); }
  to   { transform: rotate(360deg) translateX(95px) rotate(-360deg); }
}

.caption {
  margin: 0;
  font-size: 12px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: #c7d2fe;
}

@media (prefers-reduced-motion: reduce) {
  .blob { animation: none !important; }
  /* 静止時は少しずらして固める */
  .b0 { transform: translateX(60px); }
  .b1 { transform: translate(-55px, 30px); }
  .b2 { transform: translate(50px, -45px); }
  .b3 { transform: translate(-40px, -40px); }
}
JavaScript
// このデモはCSSアニメ+SVGフィルターのみで完結(JS不要)。
// ポインタ追従の遊びを少しだけ追加:マウス位置にコアが寄る。
const stage = document.querySelector(".blobs");
const core = document.querySelector(".core");

if (stage && core) {
  const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
  if (!reduce) {
    stage.addEventListener("pointermove", (e) => {
      const r = stage.getBoundingClientRect();
      // 中心からの相対位置を小さく反映(最大±18px)
      const dx = ((e.clientX - r.left) / r.width - 0.5) * 36;
      const dy = ((e.clientY - r.top) / r.height - 0.5) * 36;
      core.style.transform = `translate(${dx}px, ${dy}px)`;
    });
    stage.addEventListener("pointerleave", () => {
      core.style.transform = "translate(0,0)";
    });
  }
}

🤖 AIエージェント用プロンプト

このままコピーしてAIに貼り付け「追加する場所」だけ書き換えればOK
あなたは熟練のフロントエンドエンジニアです。私のWebサイトに「グーフィルター(粘性融合)」の効果を追加してください。

# 追加してほしい効果
グーフィルター(粘性融合)(SVG エフェクト)
feGaussianBlurとfeColorMatrixで要素同士が溶け合うメタボール表現。ローダーやメニュー展開の演出に最適です。

# 追加する場所
👉【ここに対象箇所を記入:例「トップのヒーローセクション」「お問い合わせボタン」「記事カードの一覧」など】

# 参考実装(この見た目・挙動を再現してください)
【HTML】
<!-- SVGグーフィルター: blur+colormatrixで要素が粘性をもって融合する -->
<div class="goo-stage">
  <!-- フィルター定義(不可視) -->
  <svg class="defs" aria-hidden="true" width="0" height="0">
    <defs>
      <filter id="goo">
        <!-- ぼかして -->
        <feGaussianBlur in="SourceGraphic" stdDeviation="9" result="blur" />
        <!-- アルファのコントラストを強めて輪郭をくっつける -->
        <feColorMatrix in="blur" mode="matrix"
          values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 20 -9" result="goo" />
        <feBlend in="SourceGraphic" in2="goo" />
      </filter>
    </defs>
  </svg>

  <!-- このコンテナにフィルターをかけ、中の円が融合する -->
  <div class="blobs">
    <span class="blob b0"></span>
    <span class="blob b1"></span>
    <span class="blob b2"></span>
    <span class="blob b3"></span>
    <span class="blob core"></span>
  </div>
  <p class="caption">gooey filter — メタボール風の融合</p>
</div>

【CSS】
* { box-sizing: border-box; }
body {
  margin: 0;
  min-height: 100vh;
  display: grid;
  place-items: center;
  font-family: "Segoe UI", system-ui, sans-serif;
  background: radial-gradient(120% 120% at 50% -10%, #312e81 0%, #1e1b4b 45%, #0a0a1f 100%);
  overflow: hidden;
}
.goo-stage {
  display: grid;
  justify-items: center;
  gap: 18px;
}
.defs { position: absolute; }

/* 融合エリア。filterは親に1回だけかける */
.blobs {
  position: relative;
  width: 260px;
  height: 220px;
  filter: url(#goo);
}
.blob {
  position: absolute;
  border-radius: 50%;
  background: #f472b6;
  /* グラデで艶を出す */
  background-image: radial-gradient(circle at 35% 30%, #fbcfe8, #f472b6 45%, #c026d3 100%);
  top: 50%;
  left: 50%;
}
/* 中央のコア */
.core {
  width: 90px; height: 90px;
  margin: -45px 0 0 -45px;
}
/* 周回する4つの粒。サイズと半径・速度をずらす */
.b0 { width: 46px; height: 46px; margin: -23px 0 0 -23px; animation: orbit 4.5s linear infinite; }
.b1 { width: 38px; height: 38px; margin: -19px 0 0 -19px; animation: orbit 6s linear infinite reverse; }
.b2 { width: 54px; height: 54px; margin: -27px 0 0 -27px; animation: orbit2 5.2s linear infinite; }
.b3 { width: 30px; height: 30px; margin: -15px 0 0 -15px; animation: orbit2 7s linear infinite reverse; }

@keyframes orbit {
  from { transform: rotate(0deg) translateX(70px) rotate(0deg); }
  to   { transform: rotate(360deg) translateX(70px) rotate(-360deg); }
}
@keyframes orbit2 {
  from { transform: rotate(0deg) translateX(95px) rotate(0deg); }
  to   { transform: rotate(360deg) translateX(95px) rotate(-360deg); }
}

.caption {
  margin: 0;
  font-size: 12px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: #c7d2fe;
}

@media (prefers-reduced-motion: reduce) {
  .blob { animation: none !important; }
  /* 静止時は少しずらして固める */
  .b0 { transform: translateX(60px); }
  .b1 { transform: translate(-55px, 30px); }
  .b2 { transform: translate(50px, -45px); }
  .b3 { transform: translate(-40px, -40px); }
}

【JavaScript】
// このデモはCSSアニメ+SVGフィルターのみで完結(JS不要)。
// ポインタ追従の遊びを少しだけ追加:マウス位置にコアが寄る。
const stage = document.querySelector(".blobs");
const core = document.querySelector(".core");

if (stage && core) {
  const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
  if (!reduce) {
    stage.addEventListener("pointermove", (e) => {
      const r = stage.getBoundingClientRect();
      // 中心からの相対位置を小さく反映(最大±18px)
      const dx = ((e.clientX - r.left) / r.width - 0.5) * 36;
      const dy = ((e.clientY - r.top) / r.height - 0.5) * 36;
      core.style.transform = `translate(${dx}px, ${dy}px)`;
    });
    stage.addEventListener("pointerleave", () => {
      core.style.transform = "translate(0,0)";
    });
  }
}

# 外部ライブラリ
なし(追加ライブラリ不要)

# 守ってほしいこと
- 既存のHTML構造・レイアウト・デザインを壊さないこと。必要に応じてクラス名・色・サイズを私のサイトに合わせて調整してよい。
- クラス名やidが既存と衝突しないよう、必要なら接頭辞で名前空間を分けること。
- レスポンシブ対応と prefers-reduced-motion への配慮を入れること。
- 私のサイトのフレームワーク(React / Vue / 素のHTML など)に合わせて実装すること。不明な場合は素のHTML/CSS/JSで提示し、組み込み手順も説明すること。
- 変更後の確認手順も簡潔に教えてください。