Guides · 5 min read · Updated

How do you add three.js or WebGL to a Lovable site?

Ask Lovable for a single fixed canvas component behind your page content, built with three.js, with resize handling, a capped pixel ratio, pausing when hidden and a still frame for reduced motion. Then read the generated component, test it on a phone and fix what it skipped, one change at a time.

What you are asking Lovable to build

Lovable's documentation describes it as a builder that generates an app with a frontend and editable code behind it, and it lists company websites, landing pages and campaign pages among what it makes. Because the code is yours to read and edit, you can add three.js the same way you would in any web project, either by asking in the chat or by editing the file yourself.

The shape to ask for is the one that works everywhere: one canvas fixed behind the page, one renderer, and your real headline, copy and buttons as ordinary HTML on top. The canvas is atmosphere. The words stay in the page, where search engines read them and screen readers announce them.

Lovable's docs do not spell out every detail of the project it generates, such as the exact framework version, so check your own project before you paste anything. Open the code view, find the file that holds your page, and see how the other components are written. Match that style. Reading before pasting also teaches you where things live, which makes every later request shorter and more exact.

Step 1: write the prompt

Lovable's prompting guide recommends building one section at a time, setting boundaries on what to change and making one change at a time. Apply that here: ask for the canvas on its own, with the page content untouched.

lovable-prompt.txt
Add a three.js background to the hero. Do not change any copy, layout or colors.

Create one component, SceneBackground, with a single fixed canvas behind the page.
- Use the three package. Add it as a dependency if it is missing.
- Render a slowly rotating torus knot with MeshNormalMaterial.
- Resize with a ResizeObserver, and cap the pixel ratio at 2.
- Stop rendering when the tab is hidden or the canvas is offscreen.
- If prefers-reduced-motion is on, render one still frame and do not animate.
- The canvas has pointer-events: none and aria-hidden.
- Dispose of the geometry, material and renderer when the component unmounts.

When you finish, tell me which file holds the component and which line sets the rotation speed.

Press send, wait for the build, and check the preview before you ask for anything else. If the build fails, paste the error back and ask for a fix, rather than describing the symptom.

Step 2: read the component it wrote

Lovable will write its own version, and yours may differ. Compare it with the reference below. The reference assumes a React project with TypeScript and the three package, so confirm that in your own code first. If your project uses plain JavaScript, remove the type annotations.

SceneBackground.tsx
import { useEffect, useRef } from "react";
import * as THREE from "three";

export default function SceneBackground() {
  const ref = useRef<HTMLCanvasElement>(null);

  useEffect(() => {
    const canvas = ref.current;
    if (!canvas) return;
    const reduced = matchMedia("(prefers-reduced-motion: reduce)").matches;

    const renderer = new THREE.WebGLRenderer({ canvas, antialias: true, alpha: true });
    const scene = new THREE.Scene();
    const camera = new THREE.PerspectiveCamera(45, 1, 0.1, 100);
    camera.position.z = 6;

    const geometry = new THREE.TorusKnotGeometry(1.2, 0.35, 160, 24);
    const material = new THREE.MeshNormalMaterial();
    const knot = new THREE.Mesh(geometry, material);
    scene.add(knot);

    const resize = () => {
      const w = canvas.clientWidth, h = canvas.clientHeight;
      renderer.setPixelRatio(Math.min(devicePixelRatio, 2));
      renderer.setSize(w, h, false);
      camera.aspect = w / h;
      camera.updateProjectionMatrix();
      renderer.render(scene, camera); // keeps a still frame correct after a resize
    };
    const observer = new ResizeObserver(resize);
    observer.observe(canvas);
    resize();

    let visible = true;
    let tabShown = !document.hidden;
    let spin = 0;
    let last = performance.now();
    const frame = () => {
      const now = performance.now();
      spin += Math.min(now - last, 50) / 1000; // a long pause adds at most 50 ms, so no jump
      last = now;
      knot.rotation.set(spin * 0.2, spin * 0.3, 0);
      renderer.render(scene, camera);
    };
    const sync = () => renderer.setAnimationLoop(!reduced && visible && tabShown ? frame : null);

    const io = new IntersectionObserver(([e]) => { visible = e.isIntersecting; sync(); });
    io.observe(canvas);
    const onVis = () => { tabShown = !document.hidden; sync(); };
    document.addEventListener("visibilitychange", onVis);
    sync();

    return () => {
      renderer.setAnimationLoop(null);
      io.disconnect();
      observer.disconnect();
      document.removeEventListener("visibilitychange", onVis);
      geometry.dispose();
      material.dispose();
      renderer.dispose();
    };
  }, []);

  return <canvas ref={ref} aria-hidden="true" className="pointer-events-none fixed inset-0 -z-10 h-full w-full" />;
}

Three things in it are worth looking for in whatever Lovable produces. The effect returns a cleanup function, so a hot reload does not leave a second WebGL context behind. The loop is switched on and off in one place. And the reduced-motion branch draws a single frame instead of nothing, so the page still has its background.

Step 3: place it behind your content

Mount the component once, near the top of your page or layout, so it sits behind everything. The classes above use a negative z-index, which paints it below the content that follows. If your page sections have solid backgrounds, they will hide the canvas, so make the hero background transparent.

Hero.tsx
import SceneBackground from "./SceneBackground";

export default function Hero() {
  return (
    <section className="relative min-h-screen">
      <SceneBackground />
      <div className="mx-auto max-w-3xl px-6 pt-40">
        <h1 className="text-5xl font-semibold">Your headline stays real HTML</h1>
        <a href="#start" className="mt-8 inline-block rounded-full bg-white px-6 py-3 text-black">Get started</a>
      </div>
    </section>
  );
}

If the page looks blank, check three things in order: the canvas has a size, a parent is not painting over it, and the console is free of errors. Paste any console error back to Lovable with the file name.

Step 4: test it like a visitor

  1. 1Open the published preview on a real phone. Look for stutter, heat and a hero that takes a long time to appear.
  2. 2Turn on reduced motion in your system settings and reload. The knot should be still, not missing.
  3. 3Switch tabs and come back. The motion should resume without a jump.
  4. 4Scroll past the hero. The loop should stop when the canvas is off screen, which you can confirm by watching the CPU or by adding a console log in the frame function.
  5. 5Resize the window. The shape should stay round, not stretched.

If something fails, quote the specific line back: "The pixel ratio is not capped. Cap it at 2 in resize." Small, exact corrections are what the builder handles best.

Step 5: change the look in small steps

With the background working, adjust it one property at a time, and tell Lovable exactly which one. Ask for the geometry first, then the material, then the speed. A message like "make it cooler" gives the model permission to rewrite the whole file, which is how a working scene breaks.

  • Geometry: swap the torus knot for a sphere, a plane of points or an icosahedron, and keep the rest of the component as it is.
  • Material: move from the normal material to a simple lit one, with one directional light, before trying anything heavier.
  • Color: set the colors from your page palette so the scene matches the type and buttons.
  • Motion: slow the rotation, and tie one value to the scroll position only after the idle version looks right.

Lovable's guidance to make one change at a time and to bookmark working versions applies directly. When the scene works, save that version before the next experiment, so a bad change is one click to undo.

Starting from a finished scene instead

Writing a first scene is fine, and anything beyond a rotating shape is a lot to ask in a chat. If you want a specific look, such as a particle field, a shader orb or a scroll-driven camera, start from a template with a live demo you have played. Each template page has a button that opens Lovable with the full build prompt filled in, and you press send to start. Lovable's docs say prompt links never start a build on their own, and that a very long link can exceed browser limits.

After that first build works, change the brand and copy in small steps. The guide to building with AI has the order that avoids breaking the motion, and the three.js guide goes deeper on the render loop, models and lost contexts.

Limits to plan for

  • Heavy assets. A large model or a 4096 pixel texture costs far more on a phone than the file size suggests. Ask for smaller assets before you ask for more effects.
  • Many scenes. Keep to one canvas. Browsers limit how many WebGL contexts can exist at once.
  • Custom shaders. A model can write them, but tuning them is slow in a chat. Get a working version and adjust the numbers yourself in the code.
  • Hosting and export. Lovable's docs say a project's code can sync to GitHub, GitLab or Bitbucket, so you can move the work into your own repository when you want full control.

When it does not work

Most failures come from a short list of causes. Work through it in order before asking the builder for a rewrite, because a precise report gets a precise fix.

SymptomLikely causeWhat to ask
Blank area where the scene should beThe canvas has no height, or a section paints over it"Give the canvas full viewport size and make the hero background transparent."
Console says three cannot be foundThe dependency was not added"Add the three package and fix the import."
Shape looks stretchedThe camera aspect is not updated on resize"Update the camera aspect and projection matrix in the resize handler."
The page gets hot or the fan spins upThe loop never stops"Stop the loop when the tab is hidden or the canvas is offscreen."
It doubles in developmentNo cleanup on unmount"Return a cleanup function that disposes the renderer."

Keep the console open while you test, because most of these show up there first. Copy the full message, with the file name, into the chat.

Templates that do this

Live WebGL and three.js demos, with the ones tested in Lovable ranked first. Picked from the catalog when the page is built.

Questions people ask

Can Lovable build a three.js or WebGL site?+

Lovable generates editable code, so a three.js scene is possible. Ask for one fixed canvas component with resize, a pixel ratio cap, pausing offscreen and a reduced-motion fallback, then review the generated code. Check your own project for the framework and file layout before relying on an example.

Do I need to install three.js myself in Lovable?+

You can ask Lovable to add the three package as a dependency in the same prompt. If the build reports a missing import, paste the error back and ask it to fix the dependency.

Why is my three.js canvas blank in Lovable?+

Usually the canvas has no size, a parent with a solid background covers it, or a console error stopped the scene. Check the console, give the canvas a fixed full-size style, and make the hero background transparent.

How do I keep a Lovable WebGL site fast on phones?+

Cap the pixel ratio at 2, stop the loop when the tab is hidden or the canvas is offscreen, keep models and textures small, and test on a real phone.

Can I start from an AnimatedWebs template in Lovable?+

Yes. The Lovable button on a template page opens lovable.dev with the build prompt filled in, and you press send to start the build.

Facts checked on

What this guide says about other products and web standards comes from the official pages linked below, and those can change, so check them for the latest. The code samples are our own. What it says about AnimatedWebs comes from how our own site works.

  • Lovable builds full web applications and also websites and marketing pages, including company websites, landing pages and campaign pages. A project produces a codebase that can sync to GitHub, GitLab or Bitbucket through Git sync. Lovable docs: Welcome ↗
  • A Lovable prompt link looks like lovable.dev/#prompt=..., can hold up to 50,000 characters, never starts a build on its own, and very long URLs can exceed browser length limits. Lovable docs: Build with URL ↗
  • Lovable's prompting guide recommends building one section at a time, setting boundaries on what to change and what to leave untouched, and making one change at a time. Lovable docs: Prompting best practices ↗
  • The Intersection Observer API reports when an element enters or leaves the viewport or a root, which lets a render loop stop when its canvas is offscreen. MDN: Intersection Observer API ↗
  • The three.js project documents its API and examples on its official site. The component on this page uses its stable core classes (WebGLRenderer, PerspectiveCamera, Mesh), so check the docs for the release you install. three.js documentation ↗

Product and company names are trademarks of their owners. AnimatedWebs is independent and not affiliated with them.

See the technique running

Play the live demos, open the one you like, and start with a free template or unlock the whole library.

We use essential cookies to keep you signed in, a referral cookie only if you arrive through a partner link, and cookie-free analytics. No ads, no tracking. Cookie Policy