https://motioncanvas.online/preview/viscous-chrome-fluid
Scroll Down

Loading Canvas Shader...

Next-Gen Scroll Mechanics

Experience Beautiful Ambient Depth

This browser window is simulating a production webpage. As you scroll down, the ambient Viscous Chrome Fluid background dynamically zooms and pulls focus in real-time, creating beautiful, cinematic parallax depth.

Built for Ultra Performance

Modern web layouts require buttery-smooth animations. Our styles run completely on the GPU, avoiding CPU reflow and main-thread layout jank.

Focus Pull Parallax

Scroll depth controls blur intensity and lens scaling simultaneously to guide focus elegantly.

GPU-Accelerated

Uses hardware transforms and native filters, optimized for stable 120fps scrolling.

Highly Customizable

Adjust speed, maximum blur limit, zoom multipliers, and filters in real-time.

© 2026 MOTIONCANVAS. ALL RIGHTS RESERVED.ACTIVE BACKGROUND: VISCOUS CHROME FLUID

Viscous Chrome Fluid

🟣 Canvas

A premium, edge-to-edge liquid chrome and viscous ink simulation responsive to high-velocity mouse cursor tracking.

#chrome#fluid#canvas#interactive#minimalist#gothic
Advertisement
Sponsor Advertisement
728 × 90 | Responsive Banner

Responsive Horizontal Leaderboard

Slot ID: ca-pub-detail-below-preview

Google AdSense Placeholder

Interactive Settings

Motion PresetsCustom Tuning
Animation Speed1.0x
Blur (Aesthetic diffusion)0px
Layer Opacity100%
Scale Zoom1.0x
Rotation Angle0°
Advertisement
Sponsor Advertisement
300 × 250 | Rectangle

Medium Rectangle Block

Slot ID: ca-pub-detail-after-customization

Google AdSense Placeholder

Quick Copy Actions

Download File Packages

Keyboard Shortcuts

Space Play/Pause
F Fullscreen
R Reset Slider
C Copy Code
Advertisement
Sponsor Advertisement
728 × 90 | Responsive Banner

Responsive Horizontal Leaderboard

Slot ID: ca-pub-detail-before-related

Google AdSense Placeholder

More in Abstract

Viscous Chrome Fluid - HTML5 Canvas & JavaScript Abstract background for React & Tailwind

Integrate the Viscous Chrome Fluid directly into your website. This asset is rendered using HTML5 Canvas 2D render loop. It is optimized for zero layout-shifts and runs with high-performance hardware-accelerated processing.

Performance Specifications

  • Render Mode: CANVAS (HTML5 Canvas & JavaScript)
  • Fluidity: Locked at 60fps dynamic loop
  • Bundle Footprint: Zero external NPM dependencies
  • SEO Indexing status: 100% Crawlable static semantic HTML

🛠️ Integration Capabilities

Our templates expose inline design tokens like --color-1 and --color-2 for infinite color palettes. This template is designed to fit inside hero elements, full-screen landing pages, and interactive presentation cards.

Technical Code Reference & Syntaxes for Googlebot & Crawlers

The snippets below display the direct, unminified source code utilized for rendering this background.

HTML & Inline CSS Snippet (Vanilla)

<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Viscous Chrome Fluid</title>
  <style>
    html, body {
      margin: 0;
      padding: 0;
      width: 100%;
      height: 100%;
      overflow: hidden;
      background: #09090b;
    }
    
    .viscous-chrome-container {
      width: 100%;
      height: 100%;
      background: #050505;
      position: relative;
      overflow: hidden;
    }
    #chrome-canvas {
      width: 100%;
      height: 100%;
      display: block;
    }
  </style>
</head>
<body>

  <div class="viscous-chrome-container" style="width: 100%; height: 100%; overflow: hidden; background: #050505;">
    <canvas id="chrome-canvas" style="width: 100%; height: 100%; display: block; filter: contrast(1.2) brightness(1.1);"></canvas>
    <script>
      (function() {
        const canvas = document.getElementById('chrome-canvas');
        if (!canvas) return;
        const ctx = canvas.getContext('2d');
        if (!ctx) return;
  
        let width = 0;
        let height = 0;
        const dpr = 0.5;
  
        let mouse = { x: -1000, y: -1000, active: false };
        let lerpedMouse = { x: -1000, y: -1000 };
  
        const blobs = [];
        const numBlobs = 6;
        const pointsPerBlob = 16;
  
        function initSimulation() {
          const rect = canvas.parentNode ? canvas.parentNode.getBoundingClientRect() : null;
          width = rect ? rect.width : window.innerWidth;
          height = rect ? rect.height : window.innerHeight;
  
          canvas.width = width * dpr;
          canvas.height = height * dpr;
  
          blobs.length = 0;
  
          for (let b = 0; b < numBlobs; b++) {
            const points = [];
            const baseRadius = Math.min(width, height) * (0.15 + b * 0.05);
            const centerX = width * (0.2 + Math.random() * 0.6);
            const centerY = height * (0.2 + Math.random() * 0.6);
  
            for (let i = 0; i < pointsPerBlob; i++) {
              const angle = (i / pointsPerBlob) * Math.PI * 2;
              const px = centerX + Math.cos(angle) * baseRadius;
              const py = centerY + Math.sin(angle) * baseRadius;
  
              points.push({
                x: px * dpr,
                y: py * dpr,
                ox: px * dpr,
                oy: py * dpr,
                vx: 0,
                vy: 0,
                noiseOffsetX: Math.random() * 1000,
                noiseOffsetY: Math.random() * 1000,
              });
            }
  
            blobs.push({
              points,
              baseRadius: baseRadius * dpr,
              centerX: centerX * dpr,
              centerY: centerY * dpr,
              speed: 0.2 + Math.random() * 0.3,
              angleOffset: Math.random() * Math.PI * 2,
            });
          }
        }
  
        initSimulation();
  
        window.addEventListener('mousemove', (e) => {
          const rect = canvas.getBoundingClientRect();
          mouse.x = (e.clientX - rect.left) * dpr;
          mouse.y = (e.clientY - rect.top) * dpr;
          mouse.active = true;
        });
  
        window.addEventListener('mouseleave', () => {
          mouse.active = false;
          mouse.x = -1000;
          mouse.y = -1000;
        });
  
        window.addEventListener('touchmove', (e) => {
          if (e.touches && e.touches[0]) {
            const rect = canvas.getBoundingClientRect();
            mouse.x = (e.touches[0].clientX - rect.left) * dpr;
            mouse.y = (e.touches[0].clientY - rect.top) * dpr;
            mouse.active = true;
          }
        });
  
        window.addEventListener('touchend', () => {
          mouse.active = false;
          mouse.x = -1000;
          mouse.y = -1000;
        });
  
        window.addEventListener('resize', () => {
          initSimulation();
        });
  
        let time = 0;
  
        function renderLoop() {
          time += 0.003;
          ctx.fillStyle = '#050505';
          ctx.fillRect(0, 0, canvas.width, canvas.height);
  
          if (mouse.active) {
            if (lerpedMouse.x === -1000) {
              lerpedMouse.x = mouse.x;
              lerpedMouse.y = mouse.y;
            } else {
              lerpedMouse.x += (mouse.x - lerpedMouse.x) * 0.15;
              lerpedMouse.y += (mouse.y - lerpedMouse.y) * 0.15;
            }
          } else {
            lerpedMouse.x = -1000;
            lerpedMouse.y = -1000;
          }
  
          const repellerRadius = 160 * dpr;
          const springK = 0.04;
          const damping = 0.88;
  
          blobs.forEach((blob) => {
            const driftRadius = 30 * dpr;
            const currentCenterX = blob.centerX + Math.cos(time * blob.speed + blob.angleOffset) * driftRadius;
            const currentCenterY = blob.centerY + Math.sin(time * blob.speed * 1.3 + blob.angleOffset) * driftRadius;
  
            blob.points.forEach((pt, pIdx) => {
              const angle = (pIdx / pointsPerBlob) * Math.PI * 2;
              const noiseFactor = 25 * dpr;
              const slowDriftX = Math.cos(time + pt.noiseOffsetX) * noiseFactor;
              const slowDriftY = Math.sin(time * 0.7 + pt.noiseOffsetY) * noiseFactor;
  
              const targetX = currentCenterX + Math.cos(angle) * blob.baseRadius + slowDriftX;
              const targetY = currentCenterY + Math.sin(angle) * blob.baseRadius + slowDriftY;
  
              pt.ox = targetX;
              pt.oy = targetY;
  
              if (lerpedMouse.x !== -1000) {
                const dx = pt.x - lerpedMouse.x;
                const dy = pt.y - lerpedMouse.y;
                const dist = Math.hypot(dx, dy);
  
                if (dist < repellerRadius && dist > 0.1) {
                  const force = (repellerRadius - dist) / repellerRadius;
                  const repulsionPower = force * force * 22 * dpr;
                  const pushAngle = Math.atan2(dy, dx);
  
                  pt.vx += Math.cos(pushAngle) * repulsionPower;
                  pt.vy += Math.sin(pushAngle) * repulsionPower;
                }
              }
  
              pt.vx += (pt.ox - pt.x) * springK;
              pt.vy += (pt.oy - pt.y) * springK;
  
              pt.vx *= damping;
              pt.vy *= damping;
  
              pt.x += pt.vx;
              pt.y += pt.vy;
            });
  
            drawChromePath(blob.points, ctx, 'outer', blob);
            drawChromePath(blob.points, ctx, 'middle', blob);
            drawChromePath(blob.points, ctx, 'highlight', blob);
            drawChromePath(blob.points, ctx, 'stroke', blob);
          });
  
          requestAnimationFrame(renderLoop);
        }
  
        function drawChromePath(points, c, layer, blob) {
          if (points.length < 3) return;
  
          c.beginPath();
          let startX = (points[0].x + points[points.length - 1].x) / 2;
          let startY = (points[0].y + points[points.length - 1].y) / 2;
          c.moveTo(startX, startY);
  
          for (let i = 0; i < points.length; i++) {
            const pCurrent = points[i];
            const pNext = points[(i + 1) % points.length];
            const xc = (pCurrent.x + pNext.x) / 2;
            const yc = (pCurrent.y + pNext.y) / 2;
            c.quadraticCurveTo(pCurrent.x, pCurrent.y, xc, yc);
          }
          c.closePath();
  
          const grad = c.createLinearGradient(
            blob.centerX - blob.baseRadius,
            blob.centerY - blob.baseRadius,
            blob.centerX + blob.baseRadius,
            blob.centerY + blob.baseRadius
          );
  
          if (layer === 'outer') {
            grad.addColorStop(0, '#000000');
            grad.addColorStop(0.3, '#101015');
            grad.addColorStop(0.5, '#404452');
            grad.addColorStop(0.55, '#121217');
            grad.addColorStop(0.8, '#08080a');
            grad.addColorStop(1, '#000000');
            c.fillStyle = grad;
            c.shadowColor = 'rgba(0,0,0,0.8)';
            c.shadowBlur = 40;
            c.fill();
            c.shadowBlur = 0;
          } else if (layer === 'middle') {
            c.save();
            c.scale(0.85, 0.85);
            c.translate(blob.centerX * 0.176, blob.centerY * 0.176);
            grad.addColorStop(0, '#ffffff');
            grad.addColorStop(0.2, '#9ca3af');
            grad.addColorStop(0.4, '#1f2937');
            grad.addColorStop(0.5, '#111827');
            grad.addColorStop(0.65, '#cbd5e1');
            grad.addColorStop(0.85, '#4b5563');
            grad.addColorStop(1, '#111115');
            c.fillStyle = grad;
            c.fill();
            c.restore();
          } else if (layer === 'highlight') {
            c.save();
            c.scale(0.68, 0.68);
            c.translate(blob.centerX * 0.47, blob.centerY * 0.47);
            const hGrad = c.createLinearGradient(
              blob.centerX - blob.baseRadius * 0.5,
              blob.centerY - blob.baseRadius * 0.5,
              blob.centerX + blob.baseRadius * 0.5,
              blob.centerY + blob.baseRadius * 0.5
            );
            hGrad.addColorStop(0, 'rgba(255,255,255,0.95)');
            hGrad.addColorStop(0.2, 'rgba(230,235,255,0.7)');
            hGrad.addColorStop(0.45, 'rgba(10,12,18,0.9)');
            hGrad.addColorStop(0.6, 'rgba(255,255,255,0.85)');
            hGrad.addColorStop(0.8, 'rgba(150,155,170,0.4)');
            hGrad.addColorStop(1, 'rgba(255,255,255,0.95)');
            c.fillStyle = hGrad;
            c.fill();
            c.restore();
          } else if (layer === 'stroke') {
            c.strokeStyle = 'rgba(255, 255, 255, 0.85)';
            c.lineWidth = 1.5;
            c.stroke();
          }
        }
  
        renderLoop();
      })();
    </script>
  </div>

  
</body>
</html>

React Component Wrapper (TSX)

import React from 'react';

export default function ViscousChromeFluidBackground() {
  

  return (
    <div 
       
      style={{ width: '100%', height: '100%', position: 'relative', overflow: 'hidden' }}
    >
      <style dangerouslySetInnerHTML={{ __html: `
        .viscous-chrome-container {
          width: 100%;
          height: 100%;
          background: #050505;
          position: relative;
          overflow: hidden;
        }
        #chrome-canvas {
          width: 100%;
          height: 100%;
          display: block;
        }
      ` }} />
      
      {/* HTML Structure */}
      <div 
        style={{ width: '100%', height: '100%' }}
        dangerouslySetInnerHTML={{ __html: `
          <div class="viscous-chrome-container" style="width: 100%; height: 100%; overflow: hidden; background: #050505;">
            <canvas id="chrome-canvas" style="width: 100%; height: 100%; display: block; filter: contrast(1.2) brightness(1.1);"></canvas>
            <script>
              (function() {
                const canvas = document.getElementById('chrome-canvas');
                if (!canvas) return;
                const ctx = canvas.getContext('2d');
                if (!ctx) return;
          
                let width = 0;
                let height = 0;
                const dpr = 0.5;
          
                let mouse = { x: -1000, y: -1000, active: false };
                let lerpedMouse = { x: -1000, y: -1000 };
          
                const blobs = [];
                const numBlobs = 6;
                const pointsPerBlob = 16;
          
                function initSimulation() {
                  const rect = canvas.parentNode ? canvas.parentNode.getBoundingClientRect() : null;
                  width = rect ? rect.width : window.innerWidth;
                  height = rect ? rect.height : window.innerHeight;
          
                  canvas.width = width * dpr;
                  canvas.height = height * dpr;
          
                  blobs.length = 0;
          
                  for (let b = 0; b < numBlobs; b++) {
                    const points = [];
                    const baseRadius = Math.min(width, height) * (0.15 + b * 0.05);
                    const centerX = width * (0.2 + Math.random() * 0.6);
                    const centerY = height * (0.2 + Math.random() * 0.6);
          
                    for (let i = 0; i < pointsPerBlob; i++) {
                      const angle = (i / pointsPerBlob) * Math.PI * 2;
                      const px = centerX + Math.cos(angle) * baseRadius;
                      const py = centerY + Math.sin(angle) * baseRadius;
          
                      points.push({
                        x: px * dpr,
                        y: py * dpr,
                        ox: px * dpr,
                        oy: py * dpr,
                        vx: 0,
                        vy: 0,
                        noiseOffsetX: Math.random() * 1000,
                        noiseOffsetY: Math.random() * 1000,
                      });
                    }
          
                    blobs.push({
                      points,
                      baseRadius: baseRadius * dpr,
                      centerX: centerX * dpr,
                      centerY: centerY * dpr,
                      speed: 0.2 + Math.random() * 0.3,
                      angleOffset: Math.random() * Math.PI * 2,
                    });
                  }
                }
          
                initSimulation();
          
                window.addEventListener('mousemove', (e) => {
                  const rect = canvas.getBoundingClientRect();
                  mouse.x = (e.clientX - rect.left) * dpr;
                  mouse.y = (e.clientY - rect.top) * dpr;
                  mouse.active = true;
                });
          
                window.addEventListener('mouseleave', () => {
                  mouse.active = false;
                  mouse.x = -1000;
                  mouse.y = -1000;
                });
          
                window.addEventListener('touchmove', (e) => {
                  if (e.touches && e.touches[0]) {
                    const rect = canvas.getBoundingClientRect();
                    mouse.x = (e.touches[0].clientX - rect.left) * dpr;
                    mouse.y = (e.touches[0].clientY - rect.top) * dpr;
                    mouse.active = true;
                  }
                });
          
                window.addEventListener('touchend', () => {
                  mouse.active = false;
                  mouse.x = -1000;
                  mouse.y = -1000;
                });
          
                window.addEventListener('resize', () => {
                  initSimulation();
                });
          
                let time = 0;
          
                function renderLoop() {
                  time += 0.003;
                  ctx.fillStyle = '#050505';
                  ctx.fillRect(0, 0, canvas.width, canvas.height);
          
                  if (mouse.active) {
                    if (lerpedMouse.x === -1000) {
                      lerpedMouse.x = mouse.x;
                      lerpedMouse.y = mouse.y;
                    } else {
                      lerpedMouse.x += (mouse.x - lerpedMouse.x) * 0.15;
                      lerpedMouse.y += (mouse.y - lerpedMouse.y) * 0.15;
                    }
                  } else {
                    lerpedMouse.x = -1000;
                    lerpedMouse.y = -1000;
                  }
          
                  const repellerRadius = 160 * dpr;
                  const springK = 0.04;
                  const damping = 0.88;
          
                  blobs.forEach((blob) => {
                    const driftRadius = 30 * dpr;
                    const currentCenterX = blob.centerX + Math.cos(time * blob.speed + blob.angleOffset) * driftRadius;
                    const currentCenterY = blob.centerY + Math.sin(time * blob.speed * 1.3 + blob.angleOffset) * driftRadius;
          
                    blob.points.forEach((pt, pIdx) => {
                      const angle = (pIdx / pointsPerBlob) * Math.PI * 2;
                      const noiseFactor = 25 * dpr;
                      const slowDriftX = Math.cos(time + pt.noiseOffsetX) * noiseFactor;
                      const slowDriftY = Math.sin(time * 0.7 + pt.noiseOffsetY) * noiseFactor;
          
                      const targetX = currentCenterX + Math.cos(angle) * blob.baseRadius + slowDriftX;
                      const targetY = currentCenterY + Math.sin(angle) * blob.baseRadius + slowDriftY;
          
                      pt.ox = targetX;
                      pt.oy = targetY;
          
                      if (lerpedMouse.x !== -1000) {
                        const dx = pt.x - lerpedMouse.x;
                        const dy = pt.y - lerpedMouse.y;
                        const dist = Math.hypot(dx, dy);
          
                        if (dist < repellerRadius && dist > 0.1) {
                          const force = (repellerRadius - dist) / repellerRadius;
                          const repulsionPower = force * force * 22 * dpr;
                          const pushAngle = Math.atan2(dy, dx);
          
                          pt.vx += Math.cos(pushAngle) * repulsionPower;
                          pt.vy += Math.sin(pushAngle) * repulsionPower;
                        }
                      }
          
                      pt.vx += (pt.ox - pt.x) * springK;
                      pt.vy += (pt.oy - pt.y) * springK;
          
                      pt.vx *= damping;
                      pt.vy *= damping;
          
                      pt.x += pt.vx;
                      pt.y += pt.vy;
                    });
          
                    drawChromePath(blob.points, ctx, 'outer', blob);
                    drawChromePath(blob.points, ctx, 'middle', blob);
                    drawChromePath(blob.points, ctx, 'highlight', blob);
                    drawChromePath(blob.points, ctx, 'stroke', blob);
                  });
          
                  requestAnimationFrame(renderLoop);
                }
          
                function drawChromePath(points, c, layer, blob) {
                  if (points.length < 3) return;
          
                  c.beginPath();
                  let startX = (points[0].x + points[points.length - 1].x) / 2;
                  let startY = (points[0].y + points[points.length - 1].y) / 2;
                  c.moveTo(startX, startY);
          
                  for (let i = 0; i < points.length; i++) {
                    const pCurrent = points[i];
                    const pNext = points[(i + 1) % points.length];
                    const xc = (pCurrent.x + pNext.x) / 2;
                    const yc = (pCurrent.y + pNext.y) / 2;
                    c.quadraticCurveTo(pCurrent.x, pCurrent.y, xc, yc);
                  }
                  c.closePath();
          
                  const grad = c.createLinearGradient(
                    blob.centerX - blob.baseRadius,
                    blob.centerY - blob.baseRadius,
                    blob.centerX + blob.baseRadius,
                    blob.centerY + blob.baseRadius
                  );
          
                  if (layer === 'outer') {
                    grad.addColorStop(0, '#000000');
                    grad.addColorStop(0.3, '#101015');
                    grad.addColorStop(0.5, '#404452');
                    grad.addColorStop(0.55, '#121217');
                    grad.addColorStop(0.8, '#08080a');
                    grad.addColorStop(1, '#000000');
                    c.fillStyle = grad;
                    c.shadowColor = 'rgba(0,0,0,0.8)';
                    c.shadowBlur = 40;
                    c.fill();
                    c.shadowBlur = 0;
                  } else if (layer === 'middle') {
                    c.save();
                    c.scale(0.85, 0.85);
                    c.translate(blob.centerX * 0.176, blob.centerY * 0.176);
                    grad.addColorStop(0, '#ffffff');
                    grad.addColorStop(0.2, '#9ca3af');
                    grad.addColorStop(0.4, '#1f2937');
                    grad.addColorStop(0.5, '#111827');
                    grad.addColorStop(0.65, '#cbd5e1');
                    grad.addColorStop(0.85, '#4b5563');
                    grad.addColorStop(1, '#111115');
                    c.fillStyle = grad;
                    c.fill();
                    c.restore();
                  } else if (layer === 'highlight') {
                    c.save();
                    c.scale(0.68, 0.68);
                    c.translate(blob.centerX * 0.47, blob.centerY * 0.47);
                    const hGrad = c.createLinearGradient(
                      blob.centerX - blob.baseRadius * 0.5,
                      blob.centerY - blob.baseRadius * 0.5,
                      blob.centerX + blob.baseRadius * 0.5,
                      blob.centerY + blob.baseRadius * 0.5
                    );
                    hGrad.addColorStop(0, 'rgba(255,255,255,0.95)');
                    hGrad.addColorStop(0.2, 'rgba(230,235,255,0.7)');
                    hGrad.addColorStop(0.45, 'rgba(10,12,18,0.9)');
                    hGrad.addColorStop(0.6, 'rgba(255,255,255,0.85)');
                    hGrad.addColorStop(0.8, 'rgba(150,155,170,0.4)');
                    hGrad.addColorStop(1, 'rgba(255,255,255,0.95)');
                    c.fillStyle = hGrad;
                    c.fill();
                    c.restore();
                  } else if (layer === 'stroke') {
                    c.strokeStyle = 'rgba(255, 255, 255, 0.85)';
                    c.lineWidth = 1.5;
                    c.stroke();
                  }
                }
          
                renderLoop();
              })();
            </script>
          </div>
        ` }}
      />
    </div>
  );
}

Next.js App Router Component (use client)

'use client';

import React from 'react';

export default function ViscousChromeFluidBackground() {
  

  return (
    <div 
       
      className="w-full h-full relative overflow-hidden"
    >
      <style dangerouslySetInnerHTML={{ __html: `
        .viscous-chrome-container {
          width: 100%;
          height: 100%;
          background: #050505;
          position: relative;
          overflow: hidden;
        }
        #chrome-canvas {
          width: 100%;
          height: 100%;
          display: block;
        }
      ` }} />
      
      {/* HTML Structure */}
      <div 
        className="w-full h-full"
        dangerouslySetInnerHTML={{ __html: `
          <div class="viscous-chrome-container" style="width: 100%; height: 100%; overflow: hidden; background: #050505;">
            <canvas id="chrome-canvas" style="width: 100%; height: 100%; display: block; filter: contrast(1.2) brightness(1.1);"></canvas>
            <script>
              (function() {
                const canvas = document.getElementById('chrome-canvas');
                if (!canvas) return;
                const ctx = canvas.getContext('2d');
                if (!ctx) return;
          
                let width = 0;
                let height = 0;
                const dpr = 0.5;
          
                let mouse = { x: -1000, y: -1000, active: false };
                let lerpedMouse = { x: -1000, y: -1000 };
          
                const blobs = [];
                const numBlobs = 6;
                const pointsPerBlob = 16;
          
                function initSimulation() {
                  const rect = canvas.parentNode ? canvas.parentNode.getBoundingClientRect() : null;
                  width = rect ? rect.width : window.innerWidth;
                  height = rect ? rect.height : window.innerHeight;
          
                  canvas.width = width * dpr;
                  canvas.height = height * dpr;
          
                  blobs.length = 0;
          
                  for (let b = 0; b < numBlobs; b++) {
                    const points = [];
                    const baseRadius = Math.min(width, height) * (0.15 + b * 0.05);
                    const centerX = width * (0.2 + Math.random() * 0.6);
                    const centerY = height * (0.2 + Math.random() * 0.6);
          
                    for (let i = 0; i < pointsPerBlob; i++) {
                      const angle = (i / pointsPerBlob) * Math.PI * 2;
                      const px = centerX + Math.cos(angle) * baseRadius;
                      const py = centerY + Math.sin(angle) * baseRadius;
          
                      points.push({
                        x: px * dpr,
                        y: py * dpr,
                        ox: px * dpr,
                        oy: py * dpr,
                        vx: 0,
                        vy: 0,
                        noiseOffsetX: Math.random() * 1000,
                        noiseOffsetY: Math.random() * 1000,
                      });
                    }
          
                    blobs.push({
                      points,
                      baseRadius: baseRadius * dpr,
                      centerX: centerX * dpr,
                      centerY: centerY * dpr,
                      speed: 0.2 + Math.random() * 0.3,
                      angleOffset: Math.random() * Math.PI * 2,
                    });
                  }
                }
          
                initSimulation();
          
                window.addEventListener('mousemove', (e) => {
                  const rect = canvas.getBoundingClientRect();
                  mouse.x = (e.clientX - rect.left) * dpr;
                  mouse.y = (e.clientY - rect.top) * dpr;
                  mouse.active = true;
                });
          
                window.addEventListener('mouseleave', () => {
                  mouse.active = false;
                  mouse.x = -1000;
                  mouse.y = -1000;
                });
          
                window.addEventListener('touchmove', (e) => {
                  if (e.touches && e.touches[0]) {
                    const rect = canvas.getBoundingClientRect();
                    mouse.x = (e.touches[0].clientX - rect.left) * dpr;
                    mouse.y = (e.touches[0].clientY - rect.top) * dpr;
                    mouse.active = true;
                  }
                });
          
                window.addEventListener('touchend', () => {
                  mouse.active = false;
                  mouse.x = -1000;
                  mouse.y = -1000;
                });
          
                window.addEventListener('resize', () => {
                  initSimulation();
                });
          
                let time = 0;
          
                function renderLoop() {
                  time += 0.003;
                  ctx.fillStyle = '#050505';
                  ctx.fillRect(0, 0, canvas.width, canvas.height);
          
                  if (mouse.active) {
                    if (lerpedMouse.x === -1000) {
                      lerpedMouse.x = mouse.x;
                      lerpedMouse.y = mouse.y;
                    } else {
                      lerpedMouse.x += (mouse.x - lerpedMouse.x) * 0.15;
                      lerpedMouse.y += (mouse.y - lerpedMouse.y) * 0.15;
                    }
                  } else {
                    lerpedMouse.x = -1000;
                    lerpedMouse.y = -1000;
                  }
          
                  const repellerRadius = 160 * dpr;
                  const springK = 0.04;
                  const damping = 0.88;
          
                  blobs.forEach((blob) => {
                    const driftRadius = 30 * dpr;
                    const currentCenterX = blob.centerX + Math.cos(time * blob.speed + blob.angleOffset) * driftRadius;
                    const currentCenterY = blob.centerY + Math.sin(time * blob.speed * 1.3 + blob.angleOffset) * driftRadius;
          
                    blob.points.forEach((pt, pIdx) => {
                      const angle = (pIdx / pointsPerBlob) * Math.PI * 2;
                      const noiseFactor = 25 * dpr;
                      const slowDriftX = Math.cos(time + pt.noiseOffsetX) * noiseFactor;
                      const slowDriftY = Math.sin(time * 0.7 + pt.noiseOffsetY) * noiseFactor;
          
                      const targetX = currentCenterX + Math.cos(angle) * blob.baseRadius + slowDriftX;
                      const targetY = currentCenterY + Math.sin(angle) * blob.baseRadius + slowDriftY;
          
                      pt.ox = targetX;
                      pt.oy = targetY;
          
                      if (lerpedMouse.x !== -1000) {
                        const dx = pt.x - lerpedMouse.x;
                        const dy = pt.y - lerpedMouse.y;
                        const dist = Math.hypot(dx, dy);
          
                        if (dist < repellerRadius && dist > 0.1) {
                          const force = (repellerRadius - dist) / repellerRadius;
                          const repulsionPower = force * force * 22 * dpr;
                          const pushAngle = Math.atan2(dy, dx);
          
                          pt.vx += Math.cos(pushAngle) * repulsionPower;
                          pt.vy += Math.sin(pushAngle) * repulsionPower;
                        }
                      }
          
                      pt.vx += (pt.ox - pt.x) * springK;
                      pt.vy += (pt.oy - pt.y) * springK;
          
                      pt.vx *= damping;
                      pt.vy *= damping;
          
                      pt.x += pt.vx;
                      pt.y += pt.vy;
                    });
          
                    drawChromePath(blob.points, ctx, 'outer', blob);
                    drawChromePath(blob.points, ctx, 'middle', blob);
                    drawChromePath(blob.points, ctx, 'highlight', blob);
                    drawChromePath(blob.points, ctx, 'stroke', blob);
                  });
          
                  requestAnimationFrame(renderLoop);
                }
          
                function drawChromePath(points, c, layer, blob) {
                  if (points.length < 3) return;
          
                  c.beginPath();
                  let startX = (points[0].x + points[points.length - 1].x) / 2;
                  let startY = (points[0].y + points[points.length - 1].y) / 2;
                  c.moveTo(startX, startY);
          
                  for (let i = 0; i < points.length; i++) {
                    const pCurrent = points[i];
                    const pNext = points[(i + 1) % points.length];
                    const xc = (pCurrent.x + pNext.x) / 2;
                    const yc = (pCurrent.y + pNext.y) / 2;
                    c.quadraticCurveTo(pCurrent.x, pCurrent.y, xc, yc);
                  }
                  c.closePath();
          
                  const grad = c.createLinearGradient(
                    blob.centerX - blob.baseRadius,
                    blob.centerY - blob.baseRadius,
                    blob.centerX + blob.baseRadius,
                    blob.centerY + blob.baseRadius
                  );
          
                  if (layer === 'outer') {
                    grad.addColorStop(0, '#000000');
                    grad.addColorStop(0.3, '#101015');
                    grad.addColorStop(0.5, '#404452');
                    grad.addColorStop(0.55, '#121217');
                    grad.addColorStop(0.8, '#08080a');
                    grad.addColorStop(1, '#000000');
                    c.fillStyle = grad;
                    c.shadowColor = 'rgba(0,0,0,0.8)';
                    c.shadowBlur = 40;
                    c.fill();
                    c.shadowBlur = 0;
                  } else if (layer === 'middle') {
                    c.save();
                    c.scale(0.85, 0.85);
                    c.translate(blob.centerX * 0.176, blob.centerY * 0.176);
                    grad.addColorStop(0, '#ffffff');
                    grad.addColorStop(0.2, '#9ca3af');
                    grad.addColorStop(0.4, '#1f2937');
                    grad.addColorStop(0.5, '#111827');
                    grad.addColorStop(0.65, '#cbd5e1');
                    grad.addColorStop(0.85, '#4b5563');
                    grad.addColorStop(1, '#111115');
                    c.fillStyle = grad;
                    c.fill();
                    c.restore();
                  } else if (layer === 'highlight') {
                    c.save();
                    c.scale(0.68, 0.68);
                    c.translate(blob.centerX * 0.47, blob.centerY * 0.47);
                    const hGrad = c.createLinearGradient(
                      blob.centerX - blob.baseRadius * 0.5,
                      blob.centerY - blob.baseRadius * 0.5,
                      blob.centerX + blob.baseRadius * 0.5,
                      blob.centerY + blob.baseRadius * 0.5
                    );
                    hGrad.addColorStop(0, 'rgba(255,255,255,0.95)');
                    hGrad.addColorStop(0.2, 'rgba(230,235,255,0.7)');
                    hGrad.addColorStop(0.45, 'rgba(10,12,18,0.9)');
                    hGrad.addColorStop(0.6, 'rgba(255,255,255,0.85)');
                    hGrad.addColorStop(0.8, 'rgba(150,155,170,0.4)');
                    hGrad.addColorStop(1, 'rgba(255,255,255,0.95)');
                    c.fillStyle = hGrad;
                    c.fill();
                    c.restore();
                  } else if (layer === 'stroke') {
                    c.strokeStyle = 'rgba(255, 255, 255, 0.85)';
                    c.lineWidth = 1.5;
                    c.stroke();
                  }
                }
          
                renderLoop();
              })();
            </script>
          </div>
        ` }}
      />
    </div>
  );
}

Raw CSS Stylesheet Snippet

.viscous-chrome-container {
  width: 100%;
  height: 100%;
  background: #050505;
  position: relative;
  overflow: hidden;
}
#chrome-canvas {
  width: 100%;
  height: 100%;
  display: block;
}