Building animated mesh gradients with whatamesh

  • — 5 min. read

  • — 1/6/2023

Mesh gradients look awesome. There's just something subtle and calming about the way the colors and shadows blend together to create an illuminating background.

I really like the creative flair they add to websites - in fact I'm using one right now on my homepage for shaunchander.me

Screen shot of the homepage for www.shaunchander.me
My homepage/portal page uses an animated mesh gradient!

Today, we're building our own animated mesh gradient with whatamesh.

🚀 Get started


ℹī¸
For this tutorial I'll be using React but the concepts below can be applied to any frontend framework or even vanilla JS.

Let's bootstrap a new React project with create-vite-app.

npm create vite@latest 

Vite will ask you a series of questions. I'm using React + Typescript w/SWC for this tutorial.

Installing TailwindCSS

Now let's get TailwindCSS installed. This step isn't required though if you do not want to use TailwindCSS in your project.

npm i -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

Don't forget to configure your tailwind.config.cjs file!

// tailwind.config.cjs

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    "./index.html",
    "./src/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}

Now let's add a tailwind.css. First create a styles directory, then add tailwind.css to it.

// tailwind.css

@tailwind base;
@tailwind components;
@tailwind utilities;

Then add this tailwind.css file as an import inside of main.tsx.

// main.tsx

import "./styles/tailwind.css";

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";


// ...

Cleaning out the clutter

Let's get rid of a few files Vite ships by default. Go ahead and remove App.css index.css, and that random React logo in assets (make sure you update imports inside of main.tsx).

Then clear our your App.tsx and replace it with the following.

// App.tsx

export default = () => {
	return <div>Woah, look at this whatamesh gradient.</div>;
};

Let's verify everything is working as expected by running npm run dev and visiting our dev server. You should see something like this:

Screenshot of the dev website, it should say "Woah, look at this whatamesh gradient."

Alright, let's move onto getting whatamesh configured.

👨‍đŸ’ģ Configuring whatamesh


whatamesh is not an npm package, rather, it's a good ol' JavaScript... script.

That said, the way you're supposed to use whatamesh is by importing it from a JavaScript file. Inside of the src/assets directory, let's create a new file called gradient.js.

Then, paste the contents of the whatamesh gradient script into that file!

Now back inside of our App.tsx file, let's import the gradient by doing the following:

import { Gradient } from "./assets/gradient"

You'll notice that ./assets/gradient gets flagged by TypeScript because it doesn't have any type definitions. This is unfortunately a downside with whatamesh - it's written entirely in JS only. Lucky for us, we can quickly get around this by making our own type declaration file.

Let's add a gradient.d.ts file inside of our assets directory.

// gradient.d.ts

export declare class Gradient {
	initGradient(id: string): void;
}

For those unfamiliar with *.d.ts file, they essentially let us define TypeScript types for JavaScript files. Lucky for us we only need to call this initGradient() function, which is a member of the Gradient class. I'll leave you to digest the syntax 😉.

ℹī¸
You may need to restart the TypeScript server or your IDE to see the ./assets/gradient type error go away.

Okay TypeScript shenanigans aside, let's actually get to using whatamesh.

To get started, you'll need to include a <canvas /> element somewhere on the page, this is where whatamesh will render its animated mesh gradient into.

Let's go ahead and add this into our App.tsx file.

// App.tsx

<main>
   <div>
      <h1>
         Woah, look at this whatamesh gradient.
      </h1>
   </div>
   <canvas
      id="gradient-canvas"
      data-transition-in
      />
</main>

You'll notice that I enhanced our HTML with a <main /> wrapper tag and a new <h1 /> tag. Let's follow good HTML semantics while we're at it 😏.

Now what we need to do is add some styling into our tailwind.css file. Go ahead and throw in the following:

@layer components {
	#gradient-canvas {
		width: 100%;
		height: 100%;
		--gradient-color-1: #c3e4ff;
		--gradient-color-2: #6ec3f4;
		--gradient-color-3: #eae2ff;
		--gradient-color-4: #b9beff;
	}
}

You'll notice that this selector actually defines the gradient colors! whatamesh only has 4 color stops that you can configure. I personally recommend having 1 unique color stop and have the remaining 3 be the same color. Too many colors can lead to a pretty chaotic (and annoying) background 😅.

If you want to use my colors from my homepage:

--gradient-color-1: #07f49e;
--gradient-color-2: #1d1241;
--gradient-color-3: #1d1241;
--gradient-color-4: #1d1241;

Now that we have the <canvas /> element in place, all that's left to do is to initialize the gradient. We can do this using a useEffect hook.

// App.tsx

import { useEffect } from "react";
import { Gradient } from "./assets/gradient";

export default () => {
	
	useEffect(() => {
		const gradient = new Gradient();
		gradient.initGradient("#gradient-canvas");
	}, []);
    
	return (
        <main>
           <div>
              <h1>
                 Woah, look at this whatamesh gradient.
              </h1>
           </div>
           <canvas
              id="gradient-canvas"
              data-transition-in
              />
        </main>
    )
}

🤚 Wait! Before you check your dev server, let's actually add some quick styling to our App.tsx component!

<main className="min-h-screen flex flex-col relative bg-slate-900">
   <div className="relative z-10 flex flex-col flex-1 justify-center max-w-6xl p-10">
      <h1 className="text-7xl font-bold text-white">
         Woah, look at this whatamesh gradient.
      </h1>
   </div>
   <canvas
      id="gradient-canvas"
      className="fixed inset-0"
      data-transition-in
      />
</main>

What we've essentially done is:

  • make <main /> take up the full page height and have it be a relative container
  • give some styling to the <h1 /> text
  • make our <canvas /> element span the full width and height of the our <main /> container so the gradient looks full screen.

Okay now let's check the dev server:

😚👌 Perfect.

And... that's it! You're now up and running with whatamesh 🙌.

Here's a link to the final GitHub repository as well as a demo of what we built in action.

Shaun Chander

hey, I'm shaun

Liked what you see? Want to get more delivered right into your inbox? Well come and join my newsletter! I promise not to spam too much đŸĨē