3dCMS Knowledge Base
Open the editor
SEO & AI-Readability

How the semantic mirror works

A 3D scene is invisible to crawlers, so 3dCMS renders a full, readable HTML mirror of every page at the same URL. No cloaking.

Open in the interactive manual →

The interactive version has full-text search, a table of contents, and light/dark themes.


A WebGL scene is a black box to a search engine. It can't "see" your 3D content. 3dCMS solves this honestly: when you publish, the server renders a complete, semantic HTML mirror of every page right alongside the 3D view, at the same URL.

Bots and humans get the same content This is the opposite of cloaking. There's no user-agent sniffing, no separate "SEO build." The crawlable markup is derived from the very same content your scene displays: people get the interactive view, crawlers get the readable one, and they always match.

What each published page emits

  • Complete head metadata: title, meta description, a canonical URL, Open Graph and Twitter cards (with an auto-captured shot of your scene as the social image, falling back to your hero image), and links to the plain-text and llms.txt versions.
  • Schema.org structured data (JSON-LD): the right page type (WebPage, AboutPage, ContactPage, FAQPage, and so on), breadcrumbs, FAQ, organization info, and the page's own image, so search engines understand the page. That image is the auto-captured shot of your scene, or your hero image if the page has one and no shot. It deliberately does not fall back to the shared 3dCMS card the way the social tag does: a link preview has to show something, but a machine-readable claim about this page's content is better left unsaid than pointed at a logo.
  • A visible, crawlable body: a nav, your hero copy, content sections, testimonials, an FAQ, a gallery with captions, and links to your other pages.

There's even a built-in guarantee that every human-visible text field your scene renders also appears in the crawlable mirror: the two can't drift apart. That honesty runs both ways: a web block you've hidden, or one whose parent is hidden, is left out of the crawlable HTML as well, so the mirror never quietly publishes copy a visitor can't see.

Which page a block's words land on

In a multi-page scene, a web text, image, link or 3D text block belongs to the page it sits in, and that is the page whose crawlable document carries it. A block is bound to a page when it's parented under that page surface node, or when it's scoped to it by name. Either way, its words are written into that page's URL and nowhere else.

This matters because scoping is also what switches a block off: walk away from the room and the runtime unloads it. A paragraph a visitor can only read while standing in your Pricing room belongs on the Pricing page, not on your home page, and a crawler reading home shouldn't be told about copy nobody standing there can see.

A block bound to no page is different: nothing ever switches it off, so it really is readable from anywhere in the scene. Those are collected once, on the home page (or on the single document of a scene with no pages at all), rather than repeated on every page URL.

FAQ entries and testimonials

These two are lists rather than single fields, and they reach a page from website import, the Assistant, Claude over MCP, or a template. However they were written, the published page reads them the same way: an FAQ entry is a question and an answer, a testimonial is a quote with an optional author and role. Entries with no actual text are dropped rather than published as blanks, which is what used to leave empty FAQPage structured data in the markup and a literal "undefined" in the plain-text mirror. Pages published before that fix render correctly now without republishing.

How much text your page really gives a crawler

Because the mirror is all a crawler gets, the amount of copy in it is the amount of copy your page has, full stop. A gorgeous scene with a title and nothing else is one sentence to a search engine, to a screen reader, and to the bot that builds the little card when someone pastes your link into a chat.

So publishing counts it for you and says so. If a page carries under about 60 words of readable body text, the Share dialog comes back with an advisory naming the thinnest page and its word count. A few details worth knowing:

  • The nav does not count. It is the same list of page names on every page of your site, so counting it would make an eight-page scene with no prose look like it had two sentences of its own.
  • It counts the real mirror. The number comes from the same page model the published HTML is built from, so the advisory cannot disagree with what actually shipped.
  • It never blocks. Like the other publish advisories, it is a note on a publish that already succeeded.

The fix is ordinary writing: a Summary or Hero body on the page surface, a content section, a few FAQ entries, or a web text block with a real paragraph in it. All of it lands in the mirror, and none of it changes how your 3D scene looks.

When a URL doesn't exist

A mistyped or retired page URL under your site answers a real, site-scoped 404 that links back to your site root and tells crawlers not to index it, rather than quietly serving some other site's page at 200, which is the kind of soft-404 that gets a whole domain distrusted. And a published scene with no page nodes at all keeps the canonical URL the server gave it, instead of pointing at a page-shaped URL that doesn't exist.

Spotted something off, or want a topic covered? Email hello@3dcms.info.