I'm attempting to use cdn to import the three library as well as several add ons and N8AO. As stated in the title I'm encountering the listed error:
Uncaught TypeError: Failed to resolve module specifier "three". Relative references must start with either "/", "./", or "../"..
I'm getting the error in Chrome however I have not tried any other browser yet. I'm also using WebStorm as my development environment.
I have node.js version 22.12.0 installed however I am just scripting the entirety of the functionality in the html file rather than creating a separate javascript file for the sake of having everything in one spot.
I presume it's coming from one of the import statements given here. Also including the rest of the project as it may provide other insights:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Three.js Terrain with Grass and N8AO</title>
<style>
body { margin: 0; overflow: hidden; }
canvas { display: block; }
</style>
</head>
<body>
<script type="module">
import * as THREE from '/[email protected]/build/three.module.js';
import { OrbitControls } from '/[email protected]/examples/jsm/controls/OrbitControls.js';
import { EffectComposer } from '/[email protected]/examples/jsm/postprocessing/EffectComposer.js';
import { N8AOPass } from "@latest/dist/N8AO.js";
// Scene, Camera, Renderer
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(15, 15, 10);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
document.body.appendChild(renderer.domElement);
// Controls
const controls = new OrbitControls(camera, renderer.domElement);
controls.minPolarAngle = Math.PI / 2.5;
controls.maxPolarAngle = Math.PI / 2.5;
// Sky and lighting
scene.background = new THREE.Color(0xaabbff); // Sky color
scene.add(new THREE.AmbientLight(0x404040)); // Soft ambient light
const pointLight = new THREE.PointLight(0xffffff, 1);
pointLight.position.set(10, 10, 10);
pointLight.castShadow = true;
scene.add(pointLight);
// Terrain
const width = 100;
const terrainGeometry = new THREE.PlaneGeometry(width, width, 64, 64);
terrainGeometry.rotateX(-Math.PI / 2);
// Generate elevation for terrain
const simplex = new THREE.SimplexNoise();
for (let i = 0; i < terrainGeometry.attributes.position.count; i++) {
const x = terrainGeometry.attributes.position.array[i * 3];
const z = terrainGeometry.attributes.position.array[i * 3 + 2];
const y = 2 * simplex.noise2D(x / 10, z / 10) + 3 * simplex.noise2D(x / 15, z / 15);
terrainGeometry.attributes.position.array[i * 3 + 1] = y;
}
terrainGeometryputeVertexNormals();
const terrainMaterial = new THREE.MeshStandardMaterial({ color: 0x228b22 });
const terrain = new THREE.Mesh(terrainGeometry, terrainMaterial);
terrain.receiveShadow = true;
scene.add(terrain);
// Grass
const grassGroup = new THREE.Group();
const bladeGeometry = new THREE.PlaneGeometry(0.12, 1, 1, 4).translate(0, 0.5, 0);
const grassMaterial = new THREE.MeshStandardMaterial({ color: 0x32cd32, side: THREE.DoubleSide });
for (let i = 0; i < 50000; i++) {
const blade = new THREE.Mesh(bladeGeometry, grassMaterial);
blade.position.set(
Math.random() * width - width / 2,
0,
Math.random() * width - width / 2
);
blade.rotation.y = Math.random() * Math.PI * 2;
blade.scale.y = 0.5 + Math.random() * 0.5;
grassGroup.add(blade);
}
scene.add(grassGroup);
// Post-Processing with N8AOPass
const composer = new EffectComposer(renderer);
const n8aoPass = new N8AOPass(scene, camera, window.innerWidth, window.innerHeight);
composer.addPass(n8aoPass);
// Adjust N8AOPass Parameters
n8aoPass.configuration.aoRadius = 5.0; // Set AO radius
n8aoPass.configuration.distanceFalloff = 1.0; // Distance attenuation
n8aoPass.configuration.intensity = 3.0; // AO intensity
n8aoPass.configuration.color = new THREE.Color(0, 0, 0); // AO color
n8aoPass.configuration.halfRes = false; // Full resolution for better quality
// Animation loop
function animate() {
requestAnimationFrame(animate);
// Simulate grass swaying in the wind
grassGroup.children.forEach((blade) => {
blade.rotation.x = 0.1 * Math.sin(performance.now() / 1000 + blade.position.x);
});
composer.render();
}
animate();
// Handle resizing
window.addEventListener("resize", () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
composer.setSize(window.innerWidth, window.innerHeight);
});
</script>
</body>
</html>
The version of Chrome I am using is 131.0.6778.109
The version of Webstorm I'm using is #WS-242.21829.149
I'm attempting to use cdn to import the three library as well as several add ons and N8AO. As stated in the title I'm encountering the listed error:
Uncaught TypeError: Failed to resolve module specifier "three". Relative references must start with either "/", "./", or "../"..
I'm getting the error in Chrome however I have not tried any other browser yet. I'm also using WebStorm as my development environment.
I have node.js version 22.12.0 installed however I am just scripting the entirety of the functionality in the html file rather than creating a separate javascript file for the sake of having everything in one spot.
I presume it's coming from one of the import statements given here. Also including the rest of the project as it may provide other insights:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Three.js Terrain with Grass and N8AO</title>
<style>
body { margin: 0; overflow: hidden; }
canvas { display: block; }
</style>
</head>
<body>
<script type="module">
import * as THREE from 'https://unpkg.com/[email protected]/build/three.module.js';
import { OrbitControls } from 'https://unpkg.com/[email protected]/examples/jsm/controls/OrbitControls.js';
import { EffectComposer } from 'https://unpkg.com/[email protected]/examples/jsm/postprocessing/EffectComposer.js';
import { N8AOPass } from "https://unpkg.com/n8ao@latest/dist/N8AO.js";
// Scene, Camera, Renderer
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(15, 15, 10);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
document.body.appendChild(renderer.domElement);
// Controls
const controls = new OrbitControls(camera, renderer.domElement);
controls.minPolarAngle = Math.PI / 2.5;
controls.maxPolarAngle = Math.PI / 2.5;
// Sky and lighting
scene.background = new THREE.Color(0xaabbff); // Sky color
scene.add(new THREE.AmbientLight(0x404040)); // Soft ambient light
const pointLight = new THREE.PointLight(0xffffff, 1);
pointLight.position.set(10, 10, 10);
pointLight.castShadow = true;
scene.add(pointLight);
// Terrain
const width = 100;
const terrainGeometry = new THREE.PlaneGeometry(width, width, 64, 64);
terrainGeometry.rotateX(-Math.PI / 2);
// Generate elevation for terrain
const simplex = new THREE.SimplexNoise();
for (let i = 0; i < terrainGeometry.attributes.position.count; i++) {
const x = terrainGeometry.attributes.position.array[i * 3];
const z = terrainGeometry.attributes.position.array[i * 3 + 2];
const y = 2 * simplex.noise2D(x / 10, z / 10) + 3 * simplex.noise2D(x / 15, z / 15);
terrainGeometry.attributes.position.array[i * 3 + 1] = y;
}
terrainGeometry.computeVertexNormals();
const terrainMaterial = new THREE.MeshStandardMaterial({ color: 0x228b22 });
const terrain = new THREE.Mesh(terrainGeometry, terrainMaterial);
terrain.receiveShadow = true;
scene.add(terrain);
// Grass
const grassGroup = new THREE.Group();
const bladeGeometry = new THREE.PlaneGeometry(0.12, 1, 1, 4).translate(0, 0.5, 0);
const grassMaterial = new THREE.MeshStandardMaterial({ color: 0x32cd32, side: THREE.DoubleSide });
for (let i = 0; i < 50000; i++) {
const blade = new THREE.Mesh(bladeGeometry, grassMaterial);
blade.position.set(
Math.random() * width - width / 2,
0,
Math.random() * width - width / 2
);
blade.rotation.y = Math.random() * Math.PI * 2;
blade.scale.y = 0.5 + Math.random() * 0.5;
grassGroup.add(blade);
}
scene.add(grassGroup);
// Post-Processing with N8AOPass
const composer = new EffectComposer(renderer);
const n8aoPass = new N8AOPass(scene, camera, window.innerWidth, window.innerHeight);
composer.addPass(n8aoPass);
// Adjust N8AOPass Parameters
n8aoPass.configuration.aoRadius = 5.0; // Set AO radius
n8aoPass.configuration.distanceFalloff = 1.0; // Distance attenuation
n8aoPass.configuration.intensity = 3.0; // AO intensity
n8aoPass.configuration.color = new THREE.Color(0, 0, 0); // AO color
n8aoPass.configuration.halfRes = false; // Full resolution for better quality
// Animation loop
function animate() {
requestAnimationFrame(animate);
// Simulate grass swaying in the wind
grassGroup.children.forEach((blade) => {
blade.rotation.x = 0.1 * Math.sin(performance.now() / 1000 + blade.position.x);
});
composer.render();
}
animate();
// Handle resizing
window.addEventListener("resize", () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
composer.setSize(window.innerWidth, window.innerHeight);
});
</script>
</body>
</html>
The version of Chrome I am using is 131.0.6778.109
The version of Webstorm I'm using is #WS-242.21829.149
1 Answer
Reset to default 2if you open e.g. https://unpkg.com/n8ao@latest/dist/N8AO.js you may notice:
import {Color as $5Whe3$Color, ...a lot of stuff here} from "three";
import {Pass as $5Whe3$Pass} from "three/examples/jsm/postprocessing/Pass.js";
import {Pass as $5Whe3$Pass1} from "postprocessing";
That is the root of the error you see. A browser can import a module using a module specifier that is either an absolute URL, or a relative URL that is resolved using the base URL. To solve it you may use importmap:
<script type="importmap">
In your case that importmap should work:
<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/[email protected]/build/three.module.js",
"three/examples/": "https://unpkg.com/[email protected]/examples/",
"postprocessing" : "https://unpkg.com/[email protected]/build/index.js"
}
}
</script>
<script type="module">
// your code here
<script>