hmans.co

Introducing three-elements

I’ve released a new thing! Here it is:

three-elements logo

It’s a library that provides a Web Components layer for building Three.js applications. Wait, what? Okay, it lets you build something like this:

With code that looks like this:

<three-game autorender>
<three-scene background-color="#f2f2f2">
<three-ambient-light intensity="0.1"></three-ambient-light>

<three-directional-light
intensity="0.8"
position="5, 15, 5"
cast-shadow
>
</three-directional-light>

<three-fog near="2" far="24" color="#f2f2f2"></three-fog>

<three-mesh scale="4" ontick="rotate(this); wobble(this, 4, 0.5, 3)" cast-shadow receive-shadow>
<three-dodecahedron-buffer-geometry></three-dodecahedron-buffer-geometry>
<three-mesh-standard-material color="red"></three-mesh-standard-material>
</three-mesh>

<three-group ontick="rotate(this, 2, 1, 1.5); wobble(this, 1.5, 0.3, 1)">
<three-mesh
scale="1"
position="-4, 0, 0"
ontick="rotate(this); wobble(this)"
cast-shadow
receive-shadow
>

<three-dodecahedron-buffer-geometry></three-dodecahedron-buffer-geometry>
<three-mesh-standard-material color="#444"></three-mesh-standard-material>
</three-mesh>
</three-group>

<three-group ontick="rotate(this, 1, 1.5, 1.2); wobble(this, 1.5, 0.3, 1.1)">
<three-mesh
scale="1"
position="4, 0, 0"
ontick="rotate(this); wobble(this)"
cast-shadow
receive-shadow
>

<three-dodecahedron-buffer-geometry></three-dodecahedron-buffer-geometry>
<three-mesh-standard-material color="#444"></three-mesh-standard-material>
</three-mesh>
</three-group>

<three-orbit-controls></three-orbit-controls>
</three-scene>
</three-game>

<script>
function rotate(element, x = 0.5, y = 1, z = 0.5) {
const { object, game } = element
object.rotation.x += x * game.deltaTime
object.rotation.y += y * game.deltaTime
object.rotation.z += z * game.deltaTime
}
function wobble(element, baseScale = 1, amplitude = 0.5, frequency = 1) {
const { object, game } = element
object.scale.setScalar(baseScale + Math.cos((Date.now() * frequency) / 1000) * amplitude)
}
</script>

Hey, isn’t that like…?

What three-elements is doing isn’t entirely new, so how does it compare to other libraries in this space?

It might remind you of A-Frame, but unlike that library, it operates on a much lower level, providing what is just a super-thin wrapper around the Three.js API. Unlike A-Frame, three-elements requires you to have at least a basic understanding of how Three.js works; in turn it grants you much more immediate control over Three.js, adding just a thin veneer of convenience (see below) and then getting out of your way.

three-elements also doesn’t think of itself as a framework; beyond the built-in ticker with its callbacks, it doesn’t offer any sort of scaffolding for your code, but instead aims at integrating with existing web frameworks (of which there are so, so many) as smoothly as possible.

three-elements might also remind you of react-three-fiber. And rightfully so, considering it’s borrowing many of its core concepts – but of course it’s not a React library! If you ever wanted to use something similar to react-three-fiber, but didn’t want to commit to React, three-elements is for you. (And, yeah, you can also totally use it with React, too.)

Using with a framework (or not)

three-elements provides a set of Custom Elements and doesn’t care what framework you pair it with. Its elements are fully responsive; changes to attributes will automatically be applied to the wrapped Three.js scene objects. (Open this page in your browser’s DevTools and play around with the DOM a bit to see what this means!)

You can pair three-elements with React, Preact, Vue, Svelte, Elm, Imba, Lit-Element or any other framework you prefer, as long as it creates, updates and removes DOM elements. Build full games using jQuery? Sure, why not!

Bonus Goodies

In an attempted effort to reduce boilerplate in your projects, three-elements provides a small amount of convenience functionality that are useful in most Three.js projects.

It provides a full Ticker that lets you run per-tick code like you’re used to from the big game engines. It has support for late updates, and will soon allow you to add your own fixed- or variable ticker events (eg. for animations or physics.)

three-elements also has the beginnings of a simple render pipeline whose most noteworthy features right now are Optimized Rendering and Stacked Scenes. The goal here is to provide a simple, but solid render pipeline that will work for 80% of all projects out there, but is easy to opt-out of in case you want to go crazy with your own setup.

three-elements also hooks into DOM events and makes handling input events super easy. Want a clickable mesh? Just set its onclick attribute, and it’ll just work; all the raycasting and such that you would otherwise have to do yourself is already taken care of. (I think this is one of three-elements’ coolest features, and I definitely want to use the fact that every 3D object is also a real HTML element for some other super-cool stuff in the future, like tabindexes and accessibility. Yup! 3D objects with ARIA attributes! Let’s do this!)

Where to go from here

I’ve just pushed version 0.2.0 of three-elements. It’s the first version that feels stable enough to let other people play with it. It’s still early days, so definitely expect some bugs as well as a whole bunch of missing or broken features. Remember, if you ever feel stuck or encounter something that doesn’t work, remember that you can always drop down into imperative code and interact with your objects directly if need be. (But seriously, when you find a problem, please open an issue!)

I’m using three-elements in a real, big project – in fact, I have extracted it from that – so development will continue at a brisk pace, but I’m also looking forward so much to see what you will build with it! (If you have done so, please don’t hesitate to get in touch via Twitter or Discord!)

Links