We use cookies to enhance your experience on the site
CodeWorlds
Back to collections
Guide14 min read

Spline, or a 3D scene without writing graphics code

Spline lets you build a 3D scene in a browser and embed it on a page. React export, the real performance cost, plans, and when it actually pays off.

Spline, or a 3D scene without writing graphics code

Putting a three dimensional scene on a website used to require knowing graphics libraries, computer graphics concepts, and a good deal of patience. Spline turns that into work in a browser based editor resembling interface design tools.

You build the scene visually, add materials, lights, and interactions, then export it for embedding on a page. The result runs in a browser, reacts to the cursor and to scrolling, and requires you to write no graphics code at all.

The tool carries a cost that material about it states less plainly than it should, though, and it is a performance cost. I give it its own section, since it determines whether reaching for this is worthwhile.

What the work looks like

The editor resembles interface design tools, with a timeline and a properties panel, so somebody familiar with Figma finds their way faster than in a classic three dimensional graphics program.

You assemble a scene from primitive solids, shapes drawn on a plane and extruded into space, and models imported from outside. Materials are set with sliders, and lighting by adding sources and setting their strength.

Interactions are the most interesting part and the reason people reach for this. An event such as hovering, clicking, or scrolling triggers a transition to another scene state, described visually. Without that mechanism a three dimensional scene is decoration; with it, it becomes an interface element.

Watch the object count and texture sizes from the start. A scene running smoothly in the editor on a powerful machine can stall scrolling on a three year old phone, and the difference stays invisible until you check.

Embedding on a page

The simplest route is a ready component loading a scene from an address. You install two packages, since the runtime is a peer dependency and not every package manager pulls it in by itself.

Code
Bash
npm install @splinetool/react-spline @splinetool/runtime
Code
TypeScript
import Spline from '@splinetool/react-spline'

export function Section() {
  return <Spline scene="https://prod.spline.design/xxx/scene.splinecode" />
}

That works and carries two consequences worth knowing. The first: the scene is fetched from the vendor's servers, so load time depends on their infrastructure, and the service disappearing means an empty space on your page. The second is no control over when loading starts, unless you add it yourself.

A more deliberate arrangement loads the scene only when it is needed and shows something in the meantime.

Code
TypeScript
import dynamic from 'next/dynamic'

const Spline = dynamic(() => import('@splinetool/react-spline'), {
  ssr: false,
  loading: () => <div className="h-[500px] bg-slate-100" />,
})

Contrary to a widespread belief, disabling server side rendering is not what keeps this from crashing. The component ships marked as a client component and reaches for browser objects only inside an effect that runs after mounting, so it does not break a Next.js build. A dynamic import with server rendering off serves a different purpose here: deferring the bundle download and showing your own placeholder meanwhile. The library also has a separate @splinetool/react-spline/next entry point that server renders a blurred preview derived from the scene itself, so the space is not empty while loading.

For people working directly with graphics libraries, a variant loading a scene into your own object tree is available, letting you mix elements designed in the editor with hand written code.

The performance cost

This is the most important section here and the thing to settle before deciding.

The first item is size. A scene with a few objects, textures, and an imported model usually weighs several megabytes, meaning as much as the rest of a typical page combined. On a mobile connection that is seconds of waiting before anything appears.

The second is graphics card load. A scene renders in a loop for as long as it is visible, so it occupies the graphics processor continuously. On a phone that translates directly into battery drain and into the smoothness of scrolling the rest of the page.

The remedy is stopping the render once the scene leaves the screen.

Code
TypeScript
const container = useRef<HTMLDivElement>(null)
const [visible, setVisible] = useState(false)

useEffect(() => {
  const observer = new IntersectionObserver(
    ([entry]) => setVisible(entry.isIntersecting),
    { rootMargin: '200px' }
  )
  if (container.current) observer.observe(container.current)
  return () => observer.disconnect()
}, [])

<div ref={container} className="h-[500px]">
  {visible && <Spline scene={SCENE_URL} />}
</div>

The two hundred pixel margin in the observer settings makes the scene start loading just before it enters the viewport, so the user never sees an empty space, while nothing renders outside the frame.

The third is page quality measures. A large asset loaded up front worsens the time to displaying the main content, and an element appearing after loading shifts the layout if you reserved no space for it.

Four things limit that cost and all four deserve applying. Reserve the element's dimensions up front so the layout does not shift. Load the scene only as it approaches the viewport rather than on page entry. Stop rendering once the scene leaves the viewport or the tab loses focus. And prepare a fallback: a static image for weaker devices or for people who reduced motion in their system settings.

Take that last one seriously. A scene reacting to cursor movement causes real discomfort for some people, and checking the relevant setting is three lines of code.

It also pays to measure the effect rather than estimate it. Open the page in developer tools with the network throttled and the processor slowed, set to match an average phone. The number you see says more than any general recommendation, because it concerns your scene on your page rather than an example from documentation. That test takes five minutes and regularly ends the discussion about whether the scene stays on the landing screen.

When this makes sense

Separating the uses where the tool wins from those where it is expensive decoration pays off.

It wins on a product home page, where a user arrives once, briefly, and first impressions genuinely decide. A three dimensional product model rotated with the cursor says more than three photographs and is what people remember.

It wins when presenting physical things. A piece of furniture, a device, packaging, an item of clothing: viewing from every side replaces a gallery and reduces questions before purchase.

It also wins on event pages, portfolios, and campaigns, where the effect is part of the message and the page's lifetime is measured in weeks.

It loses anywhere delivering information quickly matters. Documentation, an admin panel, a shop with a hundred products, an application used daily by the same people. In those places a few seconds of loading and constant graphics load are a cost with no return.

It also loses with content meant to be indexed. A three dimensional scene carries no text, so to a search engine it is an empty rectangle, worth accounting for when planning what appears on a landing screen.

There is one more case worth pausing over: a page where the user is meant to fill something in or buy something. An attention grabbing effect then competes with the form for that same attention, and during a purchase every distraction works against you. A three dimensional product model beside an order form can make sense, while a three dimensional background animation above it rather less so.

How to shrink a scene

Since size is the main cost, knowing what drives it pays off, because intuition misleads here regularly.

The largest item is usually textures rather than object count. An image two thousand pixels across used as a material on a small element weighs the same as that image across the whole screen, while appearing at a fraction of that size. Reducing textures to the size at which they actually appear cuts the bundle fastest.

The second is imported models. A model downloaded from a library is often prepared for film rendering and holds tens of thousands of polygons, of which a few percent are visible on screen. Simplifying the mesh before importing is a quarter of an hour's work and usually a several fold size reduction.

The third is objects invisible in shot. A visually built scene accumulates elements that in the final view are obscured or off camera and nonetheless enter the export.

The fourth, least obvious, is the number of materials. Every distinct material is separate processing during rendering, so ten objects sharing one material costs less than ten objects with ten different ones.

Check the exported scene's size before embedding it on a page.

Code
Bash
curl -sI https://prod.spline.design/xxx/scene.splinecode |
  awk '/[Cc]ontent-[Ll]ength/ { printf "%.1f MB\n", $2/1048576 }'

That is one number worth knowing before deciding, since above a few megabytes the conversation stops being about aesthetics and starts being about whether anyone sees the page before closing the tab.

Working with a team and handing over

The tool sits between design and development, so agreeing who is responsible for what before a scene reaches a page pays off.

The designer works in the editor and publishes the scene at an address that does not change. The developer embeds that address once and does not return to it for every correction, since changes in the editor appear on the page after republishing.

That is convenient and carries one risk worth naming. A change published without agreement reaches production immediately, without review and without the person responsible for the site being able to undo it. On a serious deployment, consider serving the scene from your own infrastructure, which removes the convenience of instant publishing and gives, in return, control over what users see. Just note where the vendor placed it: code export and self hosted export are listed only on the enterprise plan with custom pricing, so the plans on the price list offer no direct route to it.

The second thing to settle is who checks performance. The designer sees the scene in the editor on powerful hardware and has no reason to suspect a problem, while the developer receives a finished address and assumes everything is fine. Testing on a weaker phone belongs to somebody, and that somebody should be named rather than assumed.

The plans and what separates them

The vendor's price list spreads capabilities across four tiers plus custom pricing, and the rate depends on whether you pay a year up front or month by month.

PlanYearly, per seatMonthly, per seatWhat it adds
Free0 USD0 USDa limited number of files, web export carrying a watermark
Hobby12 USD15 USDthe watermark leaves web exports, unlimited files, the material library
Pro20 USD25 USDApple and Android exports, the watermark leaves embeds too, unlimited projects and folders
Max120 USD150 USD10,000 credits a month, the highest AI feature limits, priority support
Enterprisecustom pricingcustom pricingcode export and self hosting, SSO sign in, file version history

Two things in that table are easy to miss. The first is the watermark split across two separate items: exporting a scene to a page is cleared by Hobby already, while embedding a scene without the mark requires Pro. The second is mobile platform export, usually attributed to "paid plans" in general, and actually available only from Pro upward.

Credits for the AI features are a separate item: 2,000 a month on Hobby, 3,000 on Pro, 10,000 on Max. The allowance is raised by buying further thousands at 5 USD a month per thousand, and that is a standing line added to the subscription rather than a one off top up. Only generating models, textures, and styles consumes credits, so on a scene assembled by hand that column does not concern you.

Spline against the alternatives

OptionStrengthWeaknessPick it when
SplineVisual work, interactions without code, React exportLarge size, graphics card loadA product page, portfolio, presentation
Graphics libraries directlyFull control, size optimisationRequires 3D graphics knowledgeAn elaborate scene or a game
A video of the animationPredictable size, works everywhereNo interactivityAn effect without user reaction
MotionLightweight, animates existing elementsTwo dimensions, not a 3D sceneInterface animation, not a model

The second row deserves considering for a scene meant to live a long time. Hand written code gives control over what enters the bundle and lets you cut size considerably, at a cost in time and knowledge.

The third row gets skipped and solves plenty of cases. If a scene does not react to the user but merely plays an animation, a recording of that animation weighs less, works on every device, and loads no graphics card.

Common mistakes

The first is embedding a scene without reserving space. An element appearing after loading shifts the layout, worsening page quality measures and annoying the user.

The second is loading the scene on page entry rather than as it approaches the viewport. Several megabytes fetched immediately delay everything else.

The third is believing the application crashes without server rendering disabled, and building the whole configuration on that. The component is a client component and reaches for browser objects only after mounting. The real oversight is different: skipping the @splinetool/react-spline/next entry point, which gives a server rendered blurred preview, and putting no placeholder of your own in its place.

The fourth is testing only on a development machine. A scene smooth on a powerful machine can stall scrolling on an older phone.

The fifth is skipping the reduced motion setting. Animations cause real discomfort for some people, and checking it is three lines of code.

The sixth is placing a scene where users come for information. Documentation and admin panels lose from it unambiguously, and the loading cost returns nothing whatsoever.

The seventh is leaving objects in the scene that are invisible in the final shot. They enter the export with everything else and raise the size, even though nobody will ever see them.

FAQ

Is Spline free?

The free plan allows creating scenes and exporting them to a page, with the export carrying the vendor's watermark and the file count limited. The watermark leaves web exports only on Hobby, meaning 12 USD per seat a month billed yearly or 15 USD billed monthly. Embedding a scene without the mark, and exporting to Apple and Android, sit higher still, on Pro at 20 USD yearly or 25 USD monthly.

How much does such a scene weigh?

Usually several megabytes, meaning as much as the rest of a typical page. That is the most important number in this decision, since on a mobile connection it translates directly into seconds of waiting before anything appears.

Does it work on phones?

It does, while loading the graphics card for as long as it is visible, which translates into battery drain and scrolling smoothness. For older devices, prepare a fallback in the form of a static image.

Is a scene visible to search engines?

Not as content. A scene carries no text, so to a search engine it is an empty area. If a landing screen should be indexed, the text must sit beside the scene rather than inside it.

When is something else better?

When the scene does not react to the user, since a recording of the same animation weighs less and works everywhere. And when a page serves quick delivery of information, since there the loading and processing cost returns nothing.

The tool and documentation sit on the project site, and the React package in the npm registry.