WebGL SEO works when you treat the canvas as decoration and the HTML as the content. Google renders JavaScript, but a <canvas> is just pixels: as Utsubo puts it, it has "zero indexable text". So every headline, product detail, link and meaningful page state has to exist as server-rendered markup before the 3D boots. Do that, and a Three.js site can rank like any other.
Why canvas SEO fails by default
Most 3D sites that don't rank fail in one of four ways:
- Text inside the scene. Headlines modeled as 3D type or drawn into textures are invisible to search engines and screen readers.
- Content that only appears after JavaScript runs. Google does render JavaScript, but rendering happens in a queue, "once Google's resources allow". Google still calls server-side or pre-rendering "a great idea", partly because not all bots run JavaScript.
- Links that aren't links. Google states it "can only discover your links if they are
<a>HTML elements with anhrefattribute". A click handler on a 3D object is not a link. - One URL for many states. If every product color or apartment opens inside the same canvas with no URL change, Google sees one page.
The HTML-first rule for Three.js SEO
Build the page as if the 3D never loads. Then add it on top.

In practice the hero markup looks like this:
<section class="hero">
<h1>Ridge 40 trail shoe</h1>
<p>Carbon plate, three colorways, built for long descents.</p>
<a href="/shop/ridge-40">Shop the Ridge 40</a>
<div class="hero-3d" aria-hidden="true" style="aspect-ratio:16/9">
<img src="/img/ridge-40-poster.webp" alt="" width="1600" height="900">
<canvas></canvas>
</div>
</section>
The H1, copy and link are real HTML. The 3D container is marked aria-hidden because it's visual, and the poster holds the space so the layout doesn't jump when the canvas mounts. If any copy exists only inside the 3D experience, Utsubo recommends a <noscript> fallback with the same headline, summary and CTA.
3D website SEO checklist
| Item | What to do | How to check |
|---|---|---|
| Rendering | Server-render or pre-render every route (Next.js, Astro and similar) | View source: is the H1 there? |
| URLs per state | One indexable URL per meaningful state, e.g. /product/x-pro?color=black |
Each state loads directly from a fresh tab |
| Links | Real <a href> for every navigation; History API, not # fragments |
Crawl with any SEO crawler |
| Meta | Unique title, description, Open Graph image per URL | Paste URL into a social preview tool |
| Structured data | Organization, Product, FAQPage as JSON-LD, matching visible content | Rich Results Test |
| Alt text | Descriptive alt on posters and meaningful images | Accessibility audit |
| Speed | LCP ≤ 2.5 s, INP ≤ 200 ms, CLS ≤ 0.1 (web.dev) | PageSpeed Insights, field data |
The URL-per-state rule matters most for configurators and real-estate sites. On an example residential project we built in Tallinn, where you hover lit windows to see each apartment, the production build would give every apartment its own server-rendered page with rooms, area, price and status in plain HTML. The facade is how people browse. The pages are what Google indexes.
Structured data deserves a note. Utsubo calls FAQPage schema "the single biggest GEO lever in 2026", GEO meaning visibility in AI search answers. That's their view, not a Google statement, but answering real questions in visible HTML is good practice either way.
Speed is part of SEO too; our 3D website performance guide covers the budget and loading order.
Check it with Search Console URL Inspection
Don't trust that it works; look at what Google sees.
- Open Search Console, paste the URL into URL Inspection.
- Click Test live URL. Google fetches the page in real time.
- Click View tested page. The HTML tab shows the rendered markup: search it for your H1, key copy and links.
- Open Screenshot to see how Google's renderer drew the page. A screenshot appears only for a successful live test.
- Check More info for JavaScript console errors and blocked resources, such as a model or script disallowed in robots.txt.
- If everything is there, Request indexing. It queues the URL but, per Google, doesn't guarantee it'll be indexed; a sitemap is better for many pages.
If the HTML tab shows a loading spinner and nothing else, you've found your problem.
Mistakes we see on 3D sites
- A loading screen as the only content. "Loading 0%" is not a great page title.
- Route changes without URL changes. Lovely transitions, one indexable page.
- Text baked into GLB models. Keep type in HTML, layered over the canvas with CSS.
- Hiding all content until a camera move finishes. Content can animate in, but it should exist in the DOM from the start.
Most of these also apply to motion-heavy sites without 3D; see animated website SEO. And if you're still choosing a stack, our guide to how a Three.js website is built explains where server rendering fits.



