Building a Dynamic, Tech-Inspired Website Background

Introduction

In the previous article, we explored the overall design concept and discussed the visual presentation and user experience of a modern tech website.

Since the initial internal design leaned toward minimalism, the team began exploring ways to enhance the overall design aesthetic. This article delves into the implementation details of creating an engaging dynamic background.

If you're interested in implementing dynamic backgrounds, this guide will walk you through building a visually stunning and functional corporate website from scratch.

Technology Stack

  • Frontend Framework: Next.js
  • UI Framework: Tailwind CSS
  • Styling: Tailwind CSS (rapid development, built-in responsive design, utility-first approach)

Why Next.js?

  1. Team Compatibility: Based on React, facilitating team collaboration.
  2. SEO and Performance: Supports Server-Side Rendering (SSR) and Static Site Generation (SSG).
  3. Powerful Routing: Dynamic routing and file-based routing provide flexibility.
  4. Built-in Optimizations: Image optimization, enternationalization, and various performance enhancements.
  5. Dynamic Content Support: Easily handles blogs, news, and other dynamic scenarios.
  6. Excellent Loading Experience: Great user experience and page load speed.

Dynamic Background Solutions

Dynamic backgrounds can significantly enhance visual appeal. Here are common implementation approaches:

  1. CSS Animation Backgrounds: Pure CSS implementation using @keyframes with gradients, position transitions, and other properties.
  2. Canvas-Based Animated Backgrounds: Using <canvas> elements with JavaScript for dynamic effects like particle systems and wave animations.
  3. Video Backgrounds: Using <video> elements to play looping video content as background.
  4. WebGL Dynamic Backgrounds: Using WebGL libraries like Three.js to render 3D dynamic backgrounds.
  5. Particle Backgrounds: Using existing particle libraries like particles.js or tsparticles for quick implementation.

How to Choose?

  • Simple Requirements: Pure CSS animations, video backgrounds.
  • Complex Interactions: Canvas animations, WebGL animations (Three.js).
  • Quick Implementation: Particle background libraries (particles.js / tsparticles).

Implementation

The following example demonstrates creating a dynamic WebGL background component with React and Tailwind CSS.

1. Create Background Component

Create a new file called AnimatedBackground.tsx:

"use client";

import dynamic from "next/dynamic";
import { Suspense, useEffect, useState } from "react";
import { BackgroundContainer } from "./BackgroundContainer";

const GradientShader = dynamic(
  () => import("shadergradient").then((mod) => mod.ShaderGradient),
  { ssr: false }
);

const SceneView = dynamic(() => import("./SceneView").then((mod) => mod.SceneView), {
  ssr: false,
  loading: () => (
    <div
      className="w-full h-full bg-cover bg-center"
      style={{ backgroundImage: "url(/images/loading-bg.png)" }}
    ></div>
  ),
});

export default function AnimatedBackground() {
  const shaderConfig: any = {
    control: "props",
    animate: "on",
    brightness: 1.2,
    cDistance: 3.6,
    cameraZoom: 1,
    color1: "#0600B8",
    color2: "#9000E3",
    color3: "#0B004F",
    envPreset: "city",
    grain: "off",
    lightType: "3d",
    reflection: 0.1,
    shader: "defaults",
    type: "waterPlane",
    uSpeed: 0.2,
    uTime: 0,
    wireframe: false,
    zoomOut: false,
    toggleAxis: false,
  };

  const [webglSupported, setWebglSupported] = useState(false);
  
  useEffect(() => {
    const canvas = document.createElement("canvas");
    const glContext =
      canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
    if (glContext) {
      console.log("WebGL is supported in this browser");
      setWebglSupported(true);
    } else {
      console.log("WebGL is not supported in this browser");
    }
  }, []);
  
  return (
    <>
      {webglSupported ? (
        <BackgroundContainer>
          <SceneView className="w-full h-full">
            <Suspense fallback={null}>
              <GradientShader {...shaderConfig} />
            </Suspense>
          </SceneView>
        </BackgroundContainer>
      ) : null}
    </>
  );
}

2. Create Container Component

Create BackgroundContainer.tsx:

"use client";

import { useRef } from "react";
import dynamic from "next/dynamic";

const ThreeScene = dynamic(() => import("./ThreeScene"), { ssr: false });

const BackgroundContainer = ({ children }: any) => {
  const containerRef = useRef<any>();

  return (
    <div
      ref={containerRef}
      className="fade-in"
      style={{
        position: "fixed",
        top: 0,
        left: 0,
        width: "100%",
        height: "100%",
        zIndex: -1,
        overflow: "auto",
        touchAction: "auto",
      }}
    >
      {children}
      <ThreeScene
        style={{
          position: "fixed",
          top: 0,
          left: 0,
          width: "100%",
          height: "100%",
          pointerEvents: "none",
        }}
        eventSource={containerRef}
        eventPrefix="client"
        pixelDensity={1}
        pointerEvents="none"
      />
    </div>
  );
};

export { BackgroundContainer };

3. Create Three.js Scene

Create ThreeScene.tsx:

"use client";

import { ShaderGradientCanvas } from "shadergradient";
import { Canvas } from "@react-three/fiber";
import { Preload } from "@react-three/drei";
import tunnel from "tunnel-rat";

const sceneTunnel = tunnel();

export default function ThreeScene({ ...props }) {
  return (
    <ShaderGradientCanvas {...props}>
      {/* @ts-ignore */}
      <sceneTunnel.Out />
      <Preload all />
    </ShaderGradientCanvas>
  );
}

4. Create Scene View Component

Create SceneView.tsx:

"use client";

import { forwardRef, Suspense, useImperativeHandle, useRef } from "react";
import {
  OrbitControls,
  PerspectiveCamera,
  View as ViewImpl,
} from "@react-three/drei";
import tunnel from "tunnel-rat";

const sceneTunnel = tunnel();

const ThreeCanvas = ({ children }: any) => {
  return <sceneTunnel.In>{children}</sceneTunnel.In>;
};

export const Common = ({ color }: any) => (
  <Suspense fallback={null}>
    {color && <color attach="background" args={[color]} />}
    <ambientLight intensity={0.5} />
    <pointLight position={[20, 30, 10]} intensity={1} />
    <pointLight position={[-10, -10, -10]} color="blue" />
    <PerspectiveCamera makeDefault fov={40} position={[0, 0, 6]} />
  </Suspense>
);

const SceneView = forwardRef(({ children, orbit, ...props }: any, ref) => {
  const localRef = useRef<any>(null);
  useImperativeHandle(ref, () => localRef.current);

  return (
    <>
      <div ref={localRef} {...props} />
      <ThreeCanvas>
        <ViewImpl track={localRef}>
          {children}
          {orbit && <OrbitControls />}
        </ViewImpl>
      </ThreeCanvas>
    </>
  );
});

SceneView.displayName = "SceneView";

export { SceneView };

5. Integrate the Background

Use the background component in app/page.tsx:

import AnimatedBackground from "@/components/AnimatedBackground";

export default function Home() {
  return (
    <>
      <AnimatedBackground></AnimatedBackground>
      <div
        className="min-h-screen bg-cover bg-center"
        style={{ backgroundImage: "url(/svg/bg_n.svg)" }}
      >
        ...page content...
      </div>
    </>
  );
}

6. Install Dependencies

To run the code, install the required packages:

pnpm add @react-three/drei @react-three/fiber shadergradient tunnel-rat

By following these steps, you can implement a high-performance, responsive dynamic background for your website. Adjust the background type and interaction complexity based on your specific requirements to make your website more visually appealing!

Customization

To configure your own dynamic gradient effect, visit shadergradient.co to customize settings. Once configured, copy the generated parameters into the shaderConfig object in AnimatedBackground.tsx to achieve your unique dynamic background effect.

Resources

Tags: nextjs tailwind-css WebGL three-js react-three-fiber

Posted on Wed, 13 May 2026 12:23:12 +0000 by abduljan