Building animated mesh gradients with whatamesh
Today we're building animated mesh gradient backgrounds using whatamesh.
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

Today, we're building our own animated mesh gradient with whatamesh.
đ Get started
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:

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 đ.
./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.