Dynamically import three.js?

Due to the setup of my js code (using barba.js in my project), I need to get three.js to work as a dynamic import rather than static.

In a nutshell I have two animations that are running diffferent versions of three.js through different means.

The first svgRenderer animation uses a top level import.
The second relies on a simple inline cdn:

<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/88/three.min.js"></script>

If I leave the import declaration at top level, on load like so:

import * as THREE from "https://cdn.skypack.dev/three@0.136.0";
import { SVGRenderer } from "https://cdn.skypack.dev/three@0.136.0/examples/jsm/renderers/SVGRenderer.js";
import { OBJLoader } from "https://cdn.skypack.dev/three@0.136.0/examples/jsm/loaders/OBJLoader.js";

…it causes an error with an animation on the subpage that uses the CDN to load Three.

The first animation uses the svgRenderer and OBJLoader, and I can only get it to load by sticking with the top level import module (‘import * as THREE’).

Comment that import * as THREE line out, and the second animation works.

So there’s a clash caused by using these two different versions of three.js.

I can’t seem to find a version of three.min.js that works either as a dynamic import or inline script for both animations.

I’ve learnt a lot investigating this, but still have a way to go before properly understanding how npm or webpack might be able to help with this clash!

If there’s a dynamic import library that’s fairly easy to use, or an async/await piece of code I could use to replace the top level import that would be great!

Hope all that makes sense (I’m still new to all this!) and thanks loads in advance!

npm create vite
cd yourprojectfolder
npm install three
npm run dev

that’s it, you don’t have to understand anything. you can’t use npm without tools, because npm is mostly cjs, not esm.

the new threejs docs recommend the same come next release Documentation: Update "Installation" guide with clearer guidance on build tools and CDNs by donmccurdy · Pull Request #25468 · mrdoob/three.js · GitHub

before properly understanding how npm or webpack might be able to help

understanding npm is as important as understanding javascript itself. npm is javascripts only eco system. the one place where all javascript devs are sharing and re-using packages. these cdns also just mirror npm.

the browser esm spec is a relatively new way to consume modules. it is still a draft spec and incomplete. it will most likely take years to settle, and nobody knows how since it was made incompatible with cjs. at this point esm tools like vite are its only lifeblood.

1 Like

Is this concerning the project you previously had trouble using mouse offsets with? How comes you’re not just using the same three js import to handle both scene animations? What are the errors you’re getting when trying to switch over to imports?

My guess is that using vite as suggested still may not solve your issue, if your animations don’t work with the same imported version of three as it stands, your going to have to make both parts of your code compatible with it either way, do you have a live example of your setup and or error logs when using the import statement?

Hi @forerunrun - you’ve hit on my issue - not all my three.js scene animations work off the same imports.

I’ve tried to replicate my setup without including all the barba.js code - if you’re not familiar with it what barba essentially does is creates a wrapper housing a container for each page, and writes ‘views’ inside each container… you can then assign separate js code to each ‘view’, so it only run when this ‘view’/page loads (via a function on enter).

To mimic this ‘code switching’ in my example, comment out each function at the top (initIndexAnims + initAboutmeAnims)

The imports at the top are what the Index animation (initIndexAnim) depends on… in my setup this code has to feature outside the ‘view’ function called by barba.js, because imports have to sit top line, rather than nested inside the view/page function… so it’s called by every page, including ‘about me’… that is, called outside the wrapper, not within the container.

(I’ve tried swapping the top line import for a dynamic import in the index view function, to no avail).

The about me page animation works off the inline script (/88/three.min.js).

I reckon, as you hint at, if I could get the about me page three.js code running off the same import as the index page one, problem solved!

As you know I’m a relative beginner, but understand that the more complex index anim needs to import not just three but also the svgrenderer and object loader… so I’m assuming it would be easier to make the more simple animation run off the top level import rather than the inline script… just not sure how to do that… even though it may be an elementary fix!

Hope all that makes sense… thanks in advance!

yes i thought so, have a look at the following sandbox, is this the behaviour you’re looking for?

essentially your code structure is quite a bit obscure, both of your html pages call the same main.js file yet you’re getting elements by id that aren’t present in both html files (maybe you’re handling this with barba.js?) it’s quite difficult to understand your setup but either way is there something stopping you from having seperate js files for each html page (home-main.js, about-main.js)?

i’ve temporarily managed the above confusion with an if statement as follows…

if (window.location.pathname === "/about-me.html") {
  initAboutmeAnims();
} else {
  initIndexAnims();
}

You also have to make an update to the shader code for the logo light effect on the about page to work with later versions of three, i’ve corrected this in the shared example, essentially

vec2 res = u_resolution / u_pxaspect;

is the problem. This changes to

vec2 res;

and in the void main function (within the shader) the res is defined as such…

void main() {
  res = u_resolution / u_pxaspect;
  vec2 uv = (gl_FragCoord.xy - 0.5 * u_resolution.xy) / min(u_resolution.y, u_resolution.x);
      
  mainRender(uv, gl_FragColor);
}

as the example above is a sandbox I’ve had to import three as a npm dependency but the code should work with one version of three (removing the three.js r88 inline script tag from the html) using the imports you were previously using…

import * as THREE from "https://cdn.skypack.dev/three@0.136.0";
import { SVGRenderer } from "https://cdn.skypack.dev/three@0.136.0/examples/jsm/renderers/SVGRenderer.js";
import { OBJLoader } from "https://cdn.skypack.dev/three@0.136.0/examples/jsm/loaders/OBJLoader.js";

the more you go on to learn the better you’ll get at seeing how a code structure can work for you but understanding the infastructure of a project so that your project flows from one environment to another is essential, there is of course more than one road to Rome but almost none of them are sinuous in respects to how you’re constructing this particular project…

You’re the man, the absolute don @forerunrun!

Thanks loads.

You’re right on the barba front… it handles one main.js file in a very similar way to your if statement… loads the main index ‘wrapper’ and then prefetches subsequent pages through the ‘container’, displaying barba ‘views’ in this container.

So you can structure code in one js file, in functions that correspond to the pages or views. Prob not the best explanation!

It allows for really smooth transition animations between pages.

An a example of a view:

barba.init({
views: [
{
namespace: “index”,
beforeEnter() {
initIndexAnims();
}
},
]

Suffice to say, you’ve saved me hours and I’m very grateful dude!

ahhh i see, yes that makes a lot more sense now, i did read back and gathered that may be the case…

always a pleasure, i’m glad you managed to make sense of it :slight_smile: :beers: