Three.js, Three-Fiber, and Spline — Which is the Best?

Testing how easy it is to learn and use Three.js, Three-Fiber, and Spline — finding how much you can create with beginner-level knowledge.

Patricia Arnedo
JavaScript in Plain English

--

An animated gif of a 3D rendered keyboard that says “Build! GO” With each key cap being pressed consecutively.
The most fun animation I made in this experiment

As an animation and design enthusiast, I’ve been learning about 3D web animations for fun (and profit?) and testing out some of the most popular tools available for the task. I started by learning three.js with vanilla JavaScript, moving on to three-fiber with React, and lastly, I tried an awesome new tool called Spline.

Here is what I built with each of those methods, how it went, and which one I ultimately recommend if you’re looking to get into in-browser 3D animation. This is going to be a broad overview of each method, but I will be releasing in-depth tutorials for each method, so stay tuned! If you are interested in getting started now, I already have a three.js foundational tutorial, which is a prerequisite to building the three.js and three-fiber animations I will talk about below.

My goal with each of these experiments is to build a simple interactive 3D animation. I want to test how easy it is to learn and use, and how much you can create with beginner-level knowledge right away.

Three.js

What is Three.js?

If you aren’t familiar, three.js is a JavaScript library and API that uses WebGL to create animations in web browsers. You can read about how it works and how to use it here at my three.js beginner’s tutorial. When used with vanilla JavaScript, the experience of using three.js is much like you would expect. It is verbose and imperative rather than declarative, making setting up scenes cumbersome. It’s easy to grasp the absolute basics like geometries, meshes, materials, and lights, but learning to create complex scenes takes substantial expertise. There is a lot of conceptual information that is not intuitive, and it can quickly become overwhelming. That being said, it is a popular tool for a reason. It has powerful capabilities and has been a long-standing industry standard that revolutionized web animations when it was developed.

Building with Three.js

After my crash course in basic concepts and use, I decided to create a 3D shape that responds to scrolling by rotating in the scroll direction (a rotating shape? Groundbreaking).

An animated gif of Meryl Streep in TheDevil Wears Prada delivering her iconic line “florals for spring? groundbreaking.”

I set the foundation for my scene; the Scene, Camera, and Renderer:

import * as THREE from "three";

const scene = new THREE.Scene();

const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);

const renderer = new THREE.WebGLRenderer({
canvas: document.querySelector("#bg"),
});

Next, I created a shape, or Mesh, consisting of a Geometry (shape vertices) and a Material (surface appearance):

const geometry = new THREE.DodecahedronGeometry(10, 0);

const material = new THREE.MeshStandardMaterial({
color: 0xfa829e,
metalness: 0.8,
roughness: 0.6,
});

const shape = new THREE.Mesh(geometry, material);

scene.add(shape);

To illuminate my scene I added light sources. You can use light helpers to see a wireframe where the light sources are located. Below are my light sources and their positions:

const ambientLight = new THREE.AmbientLight(0xf7f0e6, 4);

const pointLight1 = new THREE.PointLight(0xf7f0e6, 150);

const pointLight2 = new THREE.PointLight(0xf7f0e6, 150);

pointLight1.position.set(5, 0, 17);

pointLight2.position.set(-10, 0, -15);

scene.add(pointLight1, pointLight2, ambientLight);

Lastly, I added some event listeners and an animation loop. I added event listeners for touch screens too, since the basic scroll event handler did not work on touch screen devices:

let scrollY = window.scrollY;
let startY = 0;
const rotationSpeed = 0.07;

const moveShape = () => {
const currentScrollY = window.scrollY;
const scrollDirection = currentScrollY > scrollY ? "down" : "up";
if (scrollDirection === "up") {
shape.rotation.x += rotationSpeed;
} else {
shape.rotation.x -= rotationSpeed;
}
scrollY = currentScrollY;
};

const moveShapeTouch = (e) => {
const currentY = e.touches[0].clientY;
const deltaY = currentY - startY;

if (deltaY > 0) {
shape.rotation.x += rotationSpeed;
} else {
shape.rotation.x -= rotationSpeed;
}
};

document.addEventListener("wheel", (e) => {
e.preventDefault();
moveShape();
});

document.addEventListener("touchstart", (e) => {
startY = e.touches[0].clientY;
});

document.addEventListener("touchmove", (e) => {
e.preventDefault();
moveShapeTouch(e);
});

Just like that, we have an interactive 3D animation that will react to user input. After you understand the underlying concepts of three.js, it becomes straightforward to conceptualize how to put this simple scene together. However, I encountered a lot of bugs and issues, especially when it came to touchscreen compatibility, and overall it was a frustrating but rewarding process.

Thoughts on Three.js

Since this was my first experience with animation there was a steep learning curve. Creating scenes, shapes, and animations is straightforward, but complexity grows quickly if you try to add simple things like click or hover events. Using it feels clunky and verbose.

It feels like becoming a three.js expert would take a lot of dedication and time. Despite three.js being a powerful tool that could potentially create any type of animation, it feels overwhelming to dive in and unearth more intermediate and advanced techniques. Luckily, there are a lot of community resources and tutorials to help you along the way, both free and paid. If you have a clear idea of what you want to build it’s likely that someone out there has created resources for you.

React and Three-Fiber

What is Three-Fiber?

Three-fiber is a lightweight library that turns three.js components into easy-to-use React components. According to the three-fiber team, it is more performant than three.js alone because of React optimizations and will not lag behind any three.js updates. Its main function is to express Three.js code as JSX, so any changes to core three.js features should be reflected in three-fiber.

Three-fiber runs three.js code under the hood but makes it easy to mesh with React, so you definitely need to know how to use both three.js and React as prerequisites for this option.

Building with Three-Fiber

Like our previous animation, we will be building a simple, interactive component to get an idea of what development with this method is like, especially for beginners. Looking at the three-fiber docs, they provide a simple tutorial on how to do that right off the bat here. Previously verbose three.js code can now be represented cleanly and elegantly as React components:

import { useRef, useState } from "react";
import { useFrame } from "@react-three/fiber";

const Dodecahedron = (props) => {
const meshRef = useRef();

const [hovered, setHover] = useState(false);

const [active, setActive] = useState(false);

useFrame((state, delta) => (meshRef.current.rotation.x += delta));

return (
<mesh
{...props}
ref={meshRef}
scale={active ? 1.5 : 1}
onClick={(e) => setActive(!active)}
onPointerOver={(e) => setHover(true)}
onPointerOut={(e) => setHover(false)}
>
<dodecahedronGeometry radius={3} />
<meshStandardMaterial color={hovered ? "hotpink" : "orange"} />
</mesh>
);
};

export default Dodecahedron;

This reusable mesh can now be rendered in a canvas element that contains all of the scene details like so:

import { Canvas } from "@react-three/fiber";
import Dodecahedron from "./Dodecahedron";

const AnimationCanvas = (props) => {
return (
<Canvas style={{ width: "100%", height: "100%" }}>
<ambientLight intensity={10} />
<spotLight
position={[10, 10, 10]}
angle={0.15}
penumbra={1}
decay={0}
intensity={10}
/>
<pointLight position={[-10, -10, -10]} decay={0} intensity={Math.PI} />
<Dodecahedron position={[2, 0, 0]} />
<Dodecahedron position={[-2, 0, 0]} />
</Canvas>
);
};

export default AnimationCanvas;

Now you just add this canvas component to a parent component that will be rendered in your app:

import AnimationCanvas from "./AnimationCanvas";

function App() {
return (
<>
<AnimationCanvas />
</>
);
}

export default App;

And that’s it! three-fiber allows us to build a similar animation, but more declaratively and much more quickly while leveraging React features and syntax. Hover and click effects are easily implemented and tracked with state, as opposed to the pure three.js approach of using the Raycaster to work out what objects the mouse is hovering over.

An animated gif of two yellow rotating dodecahedrons. When they are hovered over they turn pink, and when they are clicked they double in size.

Thoughts on Three-Fiber

I was thrilled with how easy it was to use three-fiber compared to three.js. Knowing that there isn’t much ‘magic’ on top of three.js is also reassuring as I would worry about three.js becoming too abstract and harder to debug. If I had to choose whether to use three.js and JavaScript or three-fiber and React, three-fiber would undeniably win every time. It’s more beginner-friendly, and has the perfect amount of abstraction to make using it easy while still retaining three.js concepts.

Spline

What is Spline?

Spline is a browser-based 3D design and collaboration tool — like the Figma of 3D design. It aims to make 3D design easier and more accessible. It has features like real-time collaboration, physics simulations, and game controls, so it's a powerful tool despite being a relative newcomer. Spline is a for-profit company, so it’s closer to other paid tools like Figma or Squarespace. Some features are for premium members only, like access to an expanded library, removing the Spline logo on shared animations, and generating textures with AI, but the free tier is more than enough to easily create beautiful animations. This tool offers a game-changing platform that simplifies 3D design, so I honestly hope it will always be free to use.

Building with Spline

There is an immediate tradeoff when building with Spline, which is learning to use their UI rather than relying on previous programming knowledge. Three.js foundational knowledge is good to have in general, but it will not necessarily help you implement a Spline animation. Thankfully you won’t need it, since there are plenty of tutorials available from Spline and other content creators. Although the Spline community is smaller than the three.js community, Spline users are enthusiastically creating lots of resources.

My first step was following the intro Spline tutorial on their getting started page here. Immediately, this entails building something much more complex than what I built with three.js and three-fiber. I was just glad I didn't have to make another rotating shape and was happy to learn how to make a small keyboard instead.

A 3D rendering of a small keyboard with four keys. Two white keys at the top for 1 and 2 and one white key and one orange key at the bottom for 0 and GO

I followed the tutorial, which moved quickly and might be challenging for people who have never used other design tools. If you’ve used design or 3D tools before, you will probably find Spline to be intuitive and kind of fun to build with. I was pleasantly surprised. I expected the tutorial to be tedious and difficult like I’ve heard from people who are learning to use Blender. But it was straightforward, step by step, and took me from never designing 3D shapes before to easily creating a keyboard, which I customized to look like this:

An animated gif of a 3D rendered keyboard that says “Build! GO” With each key cap being pressed consecutively.

You can see and interact with the animation here. Even though the tutorial itself was only 16 minutes, it took me several hours to complete. The instructor is clearly very skilled and moves quickly, sometimes leaving you to figure out what steps he just performed, or why a certain feature isn’t working as expected. It’s straightforward enough that you can fill in the gaps yourself.

Once I completed the animation in Spline, after marveling at how easy and fun it was, I was able to put it into my React application using @spline-tool/react-spline. I found this hard to believe, but it was seriously this easy to insert a Spline scene into my app:

import Spline from "@splinetool/react-spline";

export default function Animation() {
return (
<div>
<Spline scene="<your spline React export URL here>" />
</div>
);
}

That’s it, that’s the only code snippet for this section.

The react-spline tool also provides a way to target components using an ID, but the interactive behavior I added to my animation in Spline just worked in React, so I didn’t need to use those features. It also worked on mobile, no optimizations or separate handlers needed. It worked in the browser exactly as I had designed it to in the Spline UI. It wasn’t laggy or slow to load, everything about it just worked. There are methods available to provide a programmatic interface between your code and the animation, but it’s also possible to add functionality in the Spline UI, and it will automatically work when you use your Spline URL, especially for simple animations like mine.

Thoughts on Spline

I was floored by how easy and fun it was to build in Spline, and how easy it was to insert my animation, interactive features and all, into a React app. If you are not using React, that’s ok! Spline is also compatible with three.js and vanilla JavaScript, where it can be displayed on a canvas even if you aren’t using a 3d library. It provides multiple export methods, so you can use your creations in just about any context you need to, even Apple’s new VisionOS. No, I’m not sponsored by Spline, it’s really just that cool.

A picture of the export panel for spline animations, featuring various export formats like image, video, 3D formats, code, etc.
The export panel for a Spline animation, featuring various export formats

The flexibility, ease of use, and built-in features and assets made Spline an absolute treat to build with. Unlike the other two methods, I can easily see a way forward with what I have already learned. I feel like I have a much better handle on how Spline materials, lights, animations, and shape-building work, even though I’ve only completed the introductory tutorial. Even during the tutorial, I was able to change the design of the model easily as I went along. It is truly intuitive, which is not the case for the other two options. I would need a lot more practice with more advanced features to get to this stage with three.js or three-fiber.

Which Method is best?

In case it isn’t clear from my raving review above, my favorite method of the three outlined above is absolutely and without a doubt Spline. It was the easiest, most versatile, and fun to use. Features like collaboration and sharing animations via URL make it feel like 3D design is accessible to everyone. It had the smallest learning curve since you learn as you build, and quickly gain a sense for how its features work.

That being said, There are cases where more programmatic approaches might be necessary, so you might go for three.js or three-fiber depending on your use case. My use case is learning to create animations for fun, so I can hardly advise on the best tool for more serious endeavors. It’s also worth reiterating that Spline is not open source. This is a private, for-profit product, which could no longer be free to use at some point. It’s essential to learn to use a variety of tools and have an understanding of their functionality. If you’re like me and just want to create animations for small personal projects, though, I highly recommend Spline. Happy animating!

--

--