SSAOPass example code not working in Vite environment

For some reason, if I take the SSAO code from the official Threejs examples page, I place it in a Vite project, it doesn’t work. It seems to make the screen darker, that’s about it.

Even if I use importmaps and place the example code in a script tag, so long as vite is being used to host, the code will render but there will be no SSAO effect applied.

If it helps, I get these warnings in the console when running in vite environment:

Original example:

three.js examples

My code from example (it’s the same code, but in case it’s needed):

<script type="importmap">
    {
        "imports": {
            "three": "https://cdn.jsdelivr.net/npm/three@0.184.0/build/three.module.js",
            "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.184.0/examples/jsm/"
        }
    }
    </script>

    <script type="module">
        import { EffectComposer, OutputPass, RenderPass, SSAOPass } from 'three/examples/jsm/Addons.js';

        import * as THREE from 'three';
        import { GUI } from 'three/addons/libs/lil-gui.module.min.js';

        console.log('loading:',)

        let container, stats;
        let camera, scene, renderer;
        let composer;
        let group;

        function init() {

            container = document.createElement('div');
            document.body.appendChild(container);

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

            camera = new THREE.PerspectiveCamera(65, window.innerWidth / window.innerHeight, 100, 700);
            camera.position.z = 500;

            scene = new THREE.Scene();
            scene.background = new THREE.Color(0xaaaaaa);

            scene.add(new THREE.DirectionalLight(0xffffff, 4));
            scene.add(new THREE.AmbientLight(0xffffff));

            group = new THREE.Group();
            scene.add(group);

            const geometry = new THREE.BoxGeometry(10, 10, 10);
            const material = new THREE.MeshLambertMaterial();

            const mesh = new THREE.InstancedMesh(geometry, material, 100);
            const dummy = new THREE.Object3D();
            const color = new THREE.Color();

            for (let i = 0; i < 100; i++) {

                dummy.position.x = Math.random() * 400 - 200;
                dummy.position.y = Math.random() * 400 - 200;
                dummy.position.z = Math.random() * 400 - 200;
                dummy.rotation.x = Math.random();
                dummy.rotation.y = Math.random();
                dummy.rotation.z = Math.random();

                dummy.scale.setScalar(Math.random() * 10 + 2);

                dummy.updateMatrix();
                mesh.setMatrixAt(i, dummy.matrix);

                color.setHex(Math.random() * 0xffffff);
                mesh.setColorAt(i, color);

            }

            group.add(mesh);

            // stats = new Stats();
            // container.appendChild(stats.dom);

            const width = window.innerWidth;
            const height = window.innerHeight;

            composer = new EffectComposer(renderer);

            const renderPass = new RenderPass(scene, camera);
            composer.addPass(renderPass);

            const ssaoPass = new SSAOPass(scene, camera, width, height);
            composer.addPass(ssaoPass);

            console.log('ssao:', ssaoPass);

            const outputPass = new OutputPass();
            composer.addPass(outputPass);

            // Init gui
            const gui = new GUI();

            gui.add(ssaoPass, 'output', {
                'Default': SSAOPass.OUTPUT.Default,
                'SSAO Only': SSAOPass.OUTPUT.SSAO,
                'SSAO Only + Blur': SSAOPass.OUTPUT.Blur,
                'Depth': SSAOPass.OUTPUT.Depth,
                'Normal': SSAOPass.OUTPUT.Normal
            }).onChange(function (value) {

                ssaoPass.output = value;

            });
            gui.add(ssaoPass, 'kernelRadius').min(0).max(32);
            gui.add(ssaoPass, 'minDistance').min(0.001).max(0.02);
            gui.add(ssaoPass, 'maxDistance').min(0.01).max(0.3);
            gui.add(ssaoPass, 'enabled');

            onWindowResize();

            window.addEventListener('resize', onWindowResize);

        }

        init();

        function onWindowResize() {

            const width = window.innerWidth;
            const height = window.innerHeight;

            camera.aspect = width / height;
            camera.updateProjectionMatrix();

            renderer.setSize(width, height);
            composer.setSize(width, height);

        }

        function animate() {

            // stats.begin();
            render();
            // stats.end();

        }

        function render() {

            const timer = performance.now();
            group.rotation.x = timer * 0.0002;
            group.rotation.y = timer * 0.0001;

            composer.render();

        }
    </script>

Vite version: 5.1.4
threejs version: 0.184.0

Any help would be much appreciated.

it looks like you may be importing resources from different places…

the latter should likely be…

import { EffectComposer, OutputPass, RenderPass, SSAOPass } from 'three/addons/'; 

but if you’re using vite the best option (to keep three in sync across the project) would be to remove the import map entirely and use npm install three in the terminal cd’d into your root directory… I’m not sure if this will solve your issue but it’s worth a shot first aligning your three imports to one source of truth…

This is the correct answer. Switching to three/addons instead of three/examples/jsm/Addons.js within typescript environment hosted via vite worked correctly. I can now see the ambient occlusion clearly.

Do you know why this worked? Because I’m genuinely worried about using this library now.

I checked the exports for threejs:

“three/addons” links to the same JS file I imported earlier.

I wouldn’t be too worried, once you’re accustomed with the usage it’s pretty straightforward…

as far as I know, this basically boils down to how modules are resolved in a vite environment conflicting with your import map…

^ in your import map resolves to the three.module.js file in the /build/ directory… and therefore…

the reference to the /examples/ directory under the scoped three/ definition here simply doesn’t exist and / or is actually linking to the node_modules version of three.js (if that exists) and if node_modules/three does exist you’re essentially mixing 2 three.js modules, one where three.module.js is instantiated and one where it is not (where examples/jsm is looking for internal dependencies but cannot find references as it is outside the defined scope of the first three instance) (… I think :thinking:… from my understanding (I could be wrong) )

I didn’t use import maps in vite/typescript environment.

I used import maps to show that the demo is working, but the same code, moved to a typescript environment (vite), isn’t working.

So the fix was literally to replace “three/examples/jsm/Addons.js” with “three/addons” within my mainScene.tsx file, and now the code works.

If you look at the last screenshot I shared. The import “three/addons” is pointing to “three/examples/jsm/Addons.js”. But for some reason. The import only works if I use the former and not the latter. And yet they are the same import.

ah OK that makes sense, I was getting confused as I assumed that was the code snippet you were using in vite directly…

there is a minor misunderstanding here, it actually points to ./ … examples/jsm/Addons.js “./” meaning the current directory (that the package.json is in)…

that being said it is odd as I’m sure I’ve used imports with three/examples/jsm/ in vite before without issues, maybe someone with a better understanding of three’s package structure could explain what the issue could be…

This is my first time seeing this issue as well. In fact the “three/examples/jsm/Addons.js” import was from the auto-importer that either comes with typescript or comes with vscode or I installed it as an extension.

Whichever way, both the import link shared above and “three/addons” point to the same .d.ts files and same js files. 'cause if you check the image I shared in the original post above, that’s the Package.json within the three folder in /node_modules folder. So it’s from the source, not mine.

Anyways, I’ll just continue with what I was doing. It is what it is, I guess.

it’s definitely an odd one, I recall suggesting the opposite usage in vite in an earlier thread which worked for the OP whereas three/nodes/ did not… Why I "import {If} from 'three/nodes'",its error ‘The requested module '/node_modules/.vite/deps/three_nodes.js?v=fdd2419d' does not provide an export named 'If'’ - #2 by Lawrence3DPK