three-elements 0.3

Two weeks ago, I announced the availability of three-elements, a new library that wraps Three.js in a lovely collection of reactive web components, allowing you to craft browser-based games and other 3D experiences with just HTML tags or your favorite HTML framework.

Today I’m happy to announce the release of version 0.3 of this library. The focus of this version was on refining the API further, and I’m happy to report that, with the improvements included here, it’s all beginning to feel almost… stable! But make no mistake, it is still early days. The library is being used in real projects where it’s working well, but that doesn’t necessarily mean it will for you. If you give it a go, please let me know how it worked out!

The above is a little demo where three-elements is used together with Preact. Here’s a codesandbox, check it out!

So, other than the usual assortment of bug fixes and improvements, what’s new in this release?

Reworked Ticker Events

The biggest change by far is a complete rewrite of the ticker events and callbacks system. In 0.2, we were using DOM events for these, which made for a very lovely API, but unfortunately proved to be prohibitively slow. So we ripped it all out and rewrote it! three-elements projects now make use of a much more lightweight EventEmitter implementation, and there should no longer be any sort of significant performance impact once you have a higher number of elements hooking into the ticker.

Since we’re no longer using DOM events and some frameworks apply some special DOM event handling to attributes starting with on*, the attributes and properties involved were also renamed. You will now use the tick and lateTick properties, like here with lit-html:

html`<three-mesh .tick=${(dt, { object }) => object.rotateZ(5 * dt)}></three-mesh>`

Or, when setting these callbacks as string attributes, tick and late-tick:

<three-mesh tick="object.rotateZ(2 * dt)"></three-mesh>

The string attribute method has now been upgraded to not only bind this to the element the callback is defined on, but also provide object as a shortcut to the Three.js object managed by it, and dt to get the current frame’s delta time.

Referencing Resources

Sometimes, you may want to share the same resource across a range of objects; for example, a series of meshes may all use the same geometry, or material, or both. You’ve always been able to do this by dropping into imperative code, but now you can also do it declaratively: three-elements now lets you assign any object property that is a rich object (as opposed to a scalar value) by referencing another element in the document through a CSS selector.

Here’s an example where a bunch of meshes are using the same geometry and material:

<!-- Resources -->
<three-box-buffer-geometry id="geometry"></three-box-buffer-geometry>
<three-mesh-standard-material id="material" color="#555"></three-mesh-standard-material>

<!-- Scene Contents -->
<three-mesh position="-2, 0, 0" geometry="#geometry" material="#material"></three-mesh>
<three-mesh position="0, 0, 0" geometry="#geometry" material="#material"></three-mesh>
<three-mesh position="2, 0, 0" geometry="#geometry" material="#material"></three-mesh>

“deg” Helper

Rotations in Three.js are expressed in radians, which made manually rotating things a bit awkward in plain HTML projects. Yeah, googling for “half a PI” and then copying the results worked, but… well, you know. For these cases, three-elements now has a deg helper that lets you specify rotation values in degrees instead of radians:

<three-mesh rotation.x="-90deg">...</three-mesh>

Please note that this only works in string attributes. Once you assign something directly to an element’s properties, you have to do this kind of conversion yourself (but since you’re in JS-land at that point, you have all the usual tools at your disposal, like THREE.MathUtils.degToRad.)


The above was just the most important stuff that’s new in 0.3 – there’s a number of additional tweaks and fixes. Please refer to the ChangeLog for details.

What’s Next?

Development of three-elements continues at a brisk pace, and I’m tremendously happy to see it getting such a positive response from the community. Our Discord, where we chat about cool and weird new ideas all day, keeps growing, too. Come join us!

Version 0.4 will focus on tidying up the repository itself, and converting it into a monorepo that will soon become home to a number of add-on libraries. Some of the so far largely undocumented “extra” elements provided by the library – like <three-gltf-loader> – will be extracted into separate packages that will very likely iterate at a different frequency from the main package, and also have a much stricter version requirement in their dependency to Three.js itself.

We’re also working on improving integration of three-elements into the various frameworks out there. Today, you can already use three-elements with pretty much any framework that eventually modifies the DOM, but for many of these, there’s an opportunity for us to provide some additional glue that makes the whole thing easier to use, especially with regard to providing type information.

Other things we’re currently experimenting with include a light-weight generator for three-elements-based components – making it significantly easier to hook your own code into your scene objects – and a companion library that provides an integration with cannon-es physics.

So… exciting times!