CDN only approach: not so satisfying

Hi,

I try to use a CDN only approach to simplify the organisation on the server side to a bare minimum.

Now, with skypack, past v136 we all know that the import method has changed.
I had no luck working around that, even with importmaps.

Plus some weird errors of bad overloading (leading toa crash) not happening when similar code is bundled via npm make me wonder if it is realistic to use three from a full cdn.

Plus me being more involved in the c++ field and not in node and not comfortable with the added dependency of bundling, now carrying node on my back… well, it is complicated once you need external libraries to work together.

Anyone successful with that full cdn approach?

Here is a CDN example
Threejs Boilerplate - JSFiddle
Three r138, with importmaps and uses skypack CDN

Thanks Sean; here is my full html file: not working as the console.log is not honored:

<!--
Threejs Boilerplate : https://github.com/Sean-Bradley/Three.js-TypeScript-Boilerplate
Threejs Course : https://sbcode.net/threejs/
Discount Coupons : https://sbcode.net/coupons#threejs
-->

<!-- Import maps polyfill -->
<!-- Remove this when import maps will be widely supported -->
<!DOCTYPE html>
<html>

<head>
        <meta charset="utf-8">
        <title>Test</title>
        <style>
                body {
                        margin: 0;
                }

                #instructions {
                        width: 100%;
                        height: 100%;

                        display: flex;
                        flex-direction: column;
                        justify-content: center;
                        align-items: center;

                        text-align: center;
                        font-size: 14px;
                        cursor: pointer;
                }
        </style>
</head>

<body>
        <div id="blocker">
                <div id="instructions">
                        <p style="font-size:36px">
                                Click to play
                        </p>
                        <p>
                               Hello
                        </p>
                </div>
        </div>

        <script async src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"></script>

        <script type="importmap">
        {
        "imports": {

                "three": "https://cdn.skypack.dev/three@0.138.0/build/three.module",
                "three/examples/jsm/controls/PointerLockControls.js": "https://cdn.skypack.dev/three@0.138.2/examples/jsm/controls/PointerLockControls.js",
                "three/examples/jsm/loaders/EXRLoader.js": "https://cdn.skypack.dev/three@0.138.2/examples/jsm/loaders/EXRLoader.js",
                "three/examples/jsm/postprocessing/LUTPass.js": "https://cdn.skypack.dev/three@0.138.2/examples/jsm/postprocessing/LUTPass.js",
                "three/examples/jsm/loaders/LUTCubeLoader.js": "https://cdn.skypack.dev/three@0.138.2/examples/jsm/loaders/LUTCubeLoader.js",
                "three/examples/jsm/shaders/GammaCorrectionShader.js": "https://cdn.skypack.dev/three@0.138.2/examples/jsm/shaders/GammaCorrectionShader.js",
                "three/examples/jsm/postprocessing/EffectComposer.js": "https://cdn.skypack.dev/three@0.138.2/examples/jsm/postprocessing/EffectComposer.js",
                "three/examples/jsm/postprocessing/RenderPass.js": "https://cdn.skypack.dev/three@0.138.2/examples/jsm/postprocessing/RenderPass.js",
                "three/examples/jsm/postprocessing/ShaderPass.js": "https://cdn.skypack.dev/three@0.138.2/examples/jsm/postprocessing/ShaderPass.js",
                "three/examples/jsm/nodes/Nodes.js": "https://cdn.skypack.dev/three@0.138.2/examples/jsm/nodes/Nodes.js"

                }
        }       
        </script>
        <script type="module">

                import {

                        Color,
                        Camera,
                        Material,
                        Texture,
                        ShaderMaterial,
                        UniformsUtils,
                        BackSide,
                        FrontSide,
                        DoubleSide,
                        BoxGeometry,
                        PlaneGeometry,
                        Vector3,
                        Mesh,
                        InstancedMesh,
                        Math,
                        MeshStandardMaterial,
                        MathUtils,
                        Clock,
                        Raycaster,
                        Scene,
                        PerspectiveCamera,
                        WebGLRenderer,
                        WebGLRenderTarget,
                        RGBAFormat,
                        LinearFilter,
                        sRGBEncoding,
                        ACESFilmicToneMapping,
                        GridHelper,
                        AxesHelper,
                        AmbientLight,
                        DirectionalLight,
                        BufferAttribute,
                        Matrix4,
                        BufferGeometry,
                        Object3D,
                        TextureLoader,
                        RepeatWrapping,
                        Shape,
                        MeshPhongMaterial,
                        ExtrudeGeometry,
                        Vector2,
                        Path,
                        EquirectangularReflectionMapping,


                } from 'three';



                import { PointerLockControls } from "three/examples/jsm/controls/PointerLockControls.js";
                import { EXRLoader } from "three/examples/jsm/loaders/EXRLoader.js";
                import { LUTPass } from 'three/examples/jsm/postprocessing/LUTPass.js';
                import { LUTCubeLoader } from 'three/examples/jsm/loaders/LUTCubeLoader.js';
                import { GammaCorrectionShader } from 'three/examples/jsm/shaders/GammaCorrectionShader.js';
                import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
                import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js';
                import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js';
                import * as Nodes from 'three/examples/jsm/nodes/Nodes.js';


                console.log("Can you read it in your console?")

        </script>
</body>

</html>

Nothing is thrown in the console, meaning the code as issue executing. Why?
Now, it is PointerLock not having a default error…

For your info, I use the latest Firefox. And tested in the latest Chromium.
Same result if I create a separate js file and import in the import map as:

“launcher”: “./bundle.js”

node and not comfortable with the added dependency of bundling, now carrying node on my back

you’re making it a thousand times more complex. there is no web dev without node and bundling.

bundling means running npm create vite once. and npm run dev for local developing and hot reload, npm run build for making a minified, tree-shaken distributable that only contains what it needs. there is nothing more to it, you’re set up in seconds.

the only alternative to this is to rely on unfinished browser specs that do not currently work and are not ready for production, days and weeks of manual work messing with dependencies, import maps and shaky polyfills, and/or CDNs that are offline half of the day. even skypack is just a cached CDN bundler that works around the ESM minefield. all this pain, labour and uncertainty is not worth it.

mine works for me.
I don’t know what is wrong with yours. But you do mix three.js versions in your imports, and depending on version of three.js, skypack dislikes the .js at the end. You need to experiment. Anyway, I think there is some issue with importing nodes which I don’t know enough about.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Test</title>
    <style>
      body {
        margin: 0;
      }

      #instructions {
        width: 100%;
        height: 100%;

        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;

        text-align: center;
        font-size: 14px;
        cursor: pointer;
      }
    </style>

    <script
      async
      src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"
    ></script>

    <script type="importmap">
      {
        "imports": {
          "three": "https://cdn.skypack.dev/three@0.138.0/build/three.module",
          "three/examples/jsm/controls/OrbitControls": "https://cdn.skypack.dev/three@0.138.0/examples/jsm/controls/OrbitControls",
          "three/examples/jsm/libs/stats.module": "https://cdn.skypack.dev/three@0.138.0/examples/jsm/libs/stats.module",
          "dat.gui": "https://cdn.skypack.dev/dat.gui"
        }
      }
    </script>
</head>
<body>
    <script type="module">
      import * as THREE from "three";
      import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
      import Stats from "three/examples/jsm/libs/stats.module";
      import { GUI } from "dat.gui";

      const scene = new THREE.Scene();

      const camera = new THREE.PerspectiveCamera(
        75,
        window.innerWidth / window.innerHeight,
        0.1,
        100
      );
      camera.position.z = 2;

      const renderer = new THREE.WebGLRenderer();
      renderer.setSize(window.innerWidth, window.innerHeight);
      document.body.appendChild(renderer.domElement);

      const controls = new OrbitControls(camera, renderer.domElement);
      controls.enableDamping = true;

      const geometry = new THREE.BoxGeometry();
      const material = new THREE.MeshBasicMaterial({
        color: 0x00ff00,
        wireframe: true,
      });
      const cube = new THREE.Mesh(geometry, material);
      scene.add(cube);

      window.addEventListener(
        "resize",
        () => {
          camera.aspect = window.innerWidth / window.innerHeight;
          camera.updateProjectionMatrix();
          renderer.setSize(window.innerWidth, window.innerHeight);
          render();
        },
        false
      );

      const stats = Stats();
      document.body.appendChild(stats.dom);

      const gui = new GUI();

      const cubeFolder = gui.addFolder("Cube");
      cubeFolder.add(cube.rotation, "x", 0, Math.PI * 2, 0.01);
      cubeFolder.add(cube.rotation, "y", 0, Math.PI * 2, 0.01);
      cubeFolder.add(cube.rotation, "z", 0, Math.PI * 2, 0.01);
      cubeFolder.open();

      const cameraFolder = gui.addFolder("Camera");
      cameraFolder.add(camera.position, "z", 0, 10, 0.01);
      cameraFolder.open();

      var animate = function () {
        requestAnimationFrame(animate);
        controls.update();
        render();
        stats.update();
      };

      function render() {
        renderer.render(scene, camera);
      }

      animate();
    </script>
  </body>
</html>

Ok, So I got it wrong from the start.
Sean, even your update doesn’t run here.

I will try Vite or Parcel then :wink:

Thanks guys and sorry for the ping :wink:

It works for me, see screen grab. I created a file called test.html, copied exactly from my example post above, opened in firefox.

The console errors are related to how the es-module-shims.js works.

If you want to use importmaps and a cdn, do one step at a time so you can pinpoint your issue better

Here is another example of using the pointerlock controls.

<!DOCTYPE html>
<html>
    <head>
        <title>Three.js TypeScript Tutorials by Sean Bradley : https://sbcode.net/threejs</title>
        <meta name="author" content="Sean Bradley" />
        <style>
            body {
                overflow: hidden;
                margin: 0px;
            }

            #menuPanel {
                position: absolute;
                background-color: rgba(255, 255, 255, 0.5);
                top: 0px;
                left: 0px;
                width: 100%;
                height: 100%;
            }

            #startButton {
                height: 50px;
                width: 200px;
                margin: -25px -100px;
                position: relative;
                top: 50%;
                left: 50%;
                font-size: 32px;
            }
        </style>
        <!-- Import maps polyfill -->
        <!-- Remove this when import maps will be widely supported -->
        <script
        async
        src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"
      ></script>

        <script type="importmap">
            {
                "imports": {
                  "three": "https://cdn.skypack.dev/three@0.138.0/build/three.module",
                  "three/examples/jsm/controls/PointerLockControls": "https://cdn.skypack.dev/three@0.138.0/examples/jsm/controls/PointerLockControls",
                  "three/examples/jsm/libs/stats.module": "https://cdn.skypack.dev/three@0.138.0/examples/jsm/libs/stats.module"
                }
              }
        </script>
    </head>

    <body>
        <div id="menuPanel">
            <button id="startButton">Click to Start</button>
        </div>
        <script type="module">
            import * as THREE from 'three'
            import { PointerLockControls } from 'three/examples/jsm/controls/PointerLockControls'
            import Stats from 'three/examples/jsm/libs/stats.module'
            const scene = new THREE.Scene()
            var light = new THREE.AmbientLight()
            scene.add(light)
            const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
            const renderer = new THREE.WebGLRenderer()
            renderer.setSize(window.innerWidth, window.innerHeight)
            document.body.appendChild(renderer.domElement)
            const menuPanel = document.getElementById('menuPanel')
            const startButton = document.getElementById('startButton')
            startButton.addEventListener(
                'click',
                function () {
                    controls.lock()
                },
                false
            )
            const controls = new PointerLockControls(camera, renderer.domElement)
            //controls.addEventListener('change', () => console.log("Controls Change"))
            controls.addEventListener('lock', () => (menuPanel.style.display = 'none'))
            controls.addEventListener('unlock', () => (menuPanel.style.display = 'block'))
            const planeGeometry = new THREE.PlaneGeometry(100, 100, 50, 50)
            const material = new THREE.MeshBasicMaterial({ color: 0x00ff00, wireframe: true })
            const plane = new THREE.Mesh(planeGeometry, material)
            plane.rotateX(-Math.PI / 2)
            scene.add(plane)
            let cubes = new Array()
            for (let i = 0; i < 100; i++) {
                const geo = new THREE.BoxGeometry(Math.random() * 4, Math.random() * 16, Math.random() * 4)
                const mat = new THREE.MeshBasicMaterial({ wireframe: true })
                switch (i % 3) {
                    case 0:
                        mat.color = new THREE.Color(0xff0000)
                        break
                    case 1:
                        mat.color = new THREE.Color(0xffff00)
                        break
                    case 2:
                        mat.color = new THREE.Color(0x0000ff)
                        break
                }
                const cube = new THREE.Mesh(geo, mat)
                cubes.push(cube)
            }
            cubes.forEach((c) => {
                c.position.x = Math.random() * 100 - 50
                c.position.z = Math.random() * 100 - 50
                c.geometry.computeBoundingBox()
                c.position.y = (c.geometry.boundingBox.max.y - c.geometry.boundingBox.min.y) / 2
                scene.add(c)
            })
            camera.position.y = 1
            camera.position.z = 2
            const onKeyDown = (event) => {
                switch (event.key) {
                    case 'w':
                        controls.moveForward(0.25)
                        break
                    case 'a':
                        controls.moveRight(-0.25)
                        break
                    case 's':
                        controls.moveForward(-0.25)
                        break
                    case 'd':
                        controls.moveRight(0.25)
                        break
                }
            }
            document.addEventListener('keydown', onKeyDown, false)
            window.addEventListener('resize', onWindowResize, false)
            function onWindowResize() {
                camera.aspect = window.innerWidth / window.innerHeight
                camera.updateProjectionMatrix()
                renderer.setSize(window.innerWidth, window.innerHeight)
                render()
            }
            const stats = Stats()
            document.body.appendChild(stats.dom)
            var animate = function () {
                requestAnimationFrame(animate)
                //controls.update()
                render()
                stats.update()
            }
            function render() {
                renderer.render(scene, camera)
            }
            animate()
        </script>
    </body>
</html>

Ok little ping: I now use Parcel and everything works fine;)

Let’s roll :sunglasses: