Header set Access-Control-Allow-Origin "*" Header set Access-Control-Allow-Methods "GET, POST, OPTIONS" Header set Access-Control-Allow-Headers "Content-Type" 3D: get other shapes VIEWER: add env to base & claws analyze sample code cubemap: Ideal Cubemap for Jewelry: A black/white high-contrast studio HDRI or neutral cubemap white gold surface reflects mostly white or gray tones out-of-focus bright spots on a neutral gray background—perfect for capturing sparkle without introducing color noise HOMENEW: see what can be preloaded for viewer lujo re-order form SERVER: LoadModule headers_module modules/mod_headers.so So I should change this: AllowOverride None Options None Require all granted to: AllowOverride None Options None Require all granted Header set Access-Control-Allow-Origin "https://b2b.beverlyhillsjewellers.ca" Header set Access-Control-Allow-Methods "GET, POST, OPTIONS" Header set Access-Control-Allow-Headers "Content-Type" test with curl: curl -I https://b2b.beverlyhillsjewellers.ca/images/stl/studio_small_03_1k.hdr You should see this in the output: Access-Control-Allow-Origin: * Access-Control-Allow-Methods: GET, POST, OPTIONS Access-Control-Allow-Headers: Content-Type ✅ Option 1: Use Stats.js (Easy FPS monitor for Three.js) stats.js is a lightweight library that shows real-time FPS in a corner of your canvas. 🔧 Steps to add it: Add the import (before your animate() call): import Stats from 'https://esm.sh/three@0.161.0/examples/jsm/libs/stats.module.js'; Initialize and add it to the DOM: const stats = new Stats(); document.body.appendChild(stats.dom); Inside your animate() loop, add: stats.update(); ✅ Option 2: Measure FPS manually If you don't want to use external libraries, you can measure FPS like this: let lastTime = performance.now(); let frames = 0; function animate() { requestAnimationFrame(animate); const now = performance.now(); frames++; if (now - lastTime >= 1000) { // Every 1 second console.log(`FPS: ${frames}`); frames = 0; lastTime = now; } // Your animation if (ETspinEnabled) { assembly.rotation.y += 0.01; } controls.update(); renderer.render(scene, camera); } animate(); This prints the current FPS to the browser console every second. ⚙️ Bonus: Make animation frame-rate independent If you want consistent speed on all systems, multiply the rotation by deltaTime: let clock = new THREE.Clock(); function animate() { requestAnimationFrame(animate); const delta = clock.getDelta(); // time in seconds since last frame if (ETspinEnabled) { assembly.rotation.y += delta * 1.0; // Spin at 1 rad/sec (adjust as needed) } controls.update(); renderer.render(scene, camera); } FRAGMENT SHADER: https://aurigait.com/blog/how-to-create-custom-shaders-for-a-diamond-in-three-js/ SERVING FILES LOCALLY: To store Three.js locally and serve it from your own server instead of using a CDN like https://esm.sh, you need to download the correct ES module build of Three.js and reference it with a local path. Here’s how to do it: ✅ Step-by-step: 1. Download the ES module build Go to the official GitHub repository: 📦 https://github.com/mrdoob/three.js/ Navigate to: three.js/build/three.module.js Or directly download the minified ES module here: 🔗 three.module.js (v0.161.0) You can also grab it via terminal: curl https://unpkg.com/three@0.161.0/build/three.module.js -o three.module.js 2. Save it in your project folder Example directory: /your-server-root/ index.html /js/ three.module.js 3. Update your import Now reference the local file instead of the online version: import * as THREE from './js/three.module.js'; Make sure your web server allows module loading from that path (i.e. no MIME errors). ⚠️ If you use loaders (like STLLoader) They are also modules and depend on the core Three module. Example: import { STLLoader } from './js/loaders/STLLoader.js'; Make sure to adjust paths inside the loader if needed (they often import from 'three', which you might need to patch to './three.module.js'). ✅ Recap: Download three.module.js locally. Reference it in your HTML or JS like: import * as THREE from './js/three.module.js'; Repeat for other Three.js files you use (loaders, controls, etc.). SCALE SETTINGS: ✅ Goal: You want to double the overall size (R) without changing r or height. 🔧 How to Do It (with STL Geometry in Three.js): Assuming you loaded your STL into a BufferGeometry (e.g., using STLLoader): // 1. Center the geometry to make scaling symmetrical geometry.computeBoundingBox(); geometry.center(); // 2. Create a scaling matrix const scaleMatrix = new THREE.Matrix4().makeScale(2, 1, 2); // X and Z doubled, Y unchanged // 3. Apply the scaling geometry.applyMatrix4(scaleMatrix); // 4. Recalculate normals and bounding info geometry.computeVertexNormals(); geometry.computeBoundingBox(); geometry.computeBoundingSphere(); This stretches the donut outward (twice as wide/long) without affecting: Material thickness (tube size remains same). Height (Y axis untouched). 🧪 Visual Example (Conceptually) Original torus: R = 5, r = 1 After transformation: R = 10, r = 1 You just "pull" the ring outward, but don’t thicken the material. 📝 STL File Directly? If you want to modify the STL before loading (e.g., preprocessing), you’d: Load vertices. For each vertex (x, y, z): x *= 2; z *= 2; Save new STL. If I have already loaded the stl file into gTriangles(), will these functions work as expected? TYPE VERTEX x AS SINGLE y AS SINGLE z AS SINGLE END TYPE TYPE TRIANGLE normal AS VERTEX v1 AS VERTEX v2 AS VERTEX v3 AS VERTEX END TYPE GLOBAL gTriangles() AS TRIANGLE GLOBAL gTriangleCount,origCount AS LONG LOCAL cx AS VERTEX cx = GetCenter() CALL ScaleFromCenter(cx, scale) ' === Function to compute geometric center === FUNCTION GetCenter() AS VERTEX LOCAL cx, cy, cz AS DOUBLE DIM result AS VERTEX FOR i = 0 TO origCount - 1 cx = cx + gTriangles(i).v1.x + gTriangles(i).v2.x + gTriangles(i).v3.x cy = cy + gTriangles(i).v1.y + gTriangles(i).v2.y + gTriangles(i).v3.y cz = cz + gTriangles(i).v1.z + gTriangles(i).v2.z + gTriangles(i).v3.z NEXT result.X = cx / (origCount * 3) result.Y = cy / (origCount * 3) result.Z = cz / (origCount * 3) FUNCTION = result END FUNCTION ' === Function to scale outward from center === SUB ScaleFromCenter(BYVAL center AS VERTEX, BYVAL scaleFactor AS DOUBLE) LOCAL dx, dy, dz AS DOUBLE LOCAL len AS DOUBLE FOR i = 0 TO origCount - 1 ' ---- v1 ---- dx = gTriangles(i).v1.x - center.x dy = gTriangles(i).v1.y - center.y dz = gTriangles(i).v1.z - center.z ' Compute direction vector length len = SQR(dx*dx + dy*dy + dz*dz) IF len > 0 THEN dx = dx / len: dy = dy / len: dz = dz / len ' Move vertex outward from center gTriangles(i).v1.x = center.X + dx * (len * scaleFactor) gTriangles(i).v1.y = center.Y + dy * (len * scaleFactor) gTriangles(i).v1.z = center.Z + dz * (len * scaleFactor) END IF ' ---- v2 ---- dx = gTriangles(i).v2.x - center.x dy = gTriangles(i).v2.y - center.y dz = gTriangles(i).v2.z - center.z ' Compute direction vector length len = SQR(dx*dx + dy*dy + dz*dz) IF len > 0 THEN dx = dx / len: dy = dy / len: dz = dz / len ' Move vertex outward from center gTriangles(i).v2.x = center.X + dx * (len * scaleFactor) gTriangles(i).v2.y = center.Y + dy * (len * scaleFactor) gTriangles(i).v2.z = center.Z + dz * (len * scaleFactor) END IF ' ---- v3 ---- dx = gTriangles(i).v3.x - center.x dy = gTriangles(i).v3.y - center.y dz = gTriangles(i).v3.z - center.z ' Compute direction vector length len = SQR(dx*dx + dy*dy + dz*dz) IF len > 0 THEN dx = dx / len: dy = dy / len: dz = dz / len ' Move vertex outward from center gTriangles(i).v3.x = center.X + dx * (len * scaleFactor) gTriangles(i).v3.y = center.Y + dy * (len * scaleFactor) gTriangles(i).v3.z = center.Z + dz * (len * scaleFactor) END IF NEXT END SUB If you want the parent to expose variables to the iframe if both are from the same origin, assign them like: window.someValue = ...; Then from the iframe: const val = parent.someValue; If iframe changes parent.someValue it also changes window.someValue on parent If different origins: To send data to iframe: In the parent (e.g., on parent.com): const iframe = document.querySelector("iframe"); iframe.contentWindow.postMessage({ type: "send-x", value: window.x }, "https://iframe.com"); In the iframe (e.g., on iframe.com): window.addEventListener("message", (event) => { if (event.origin !== "https://parent.com") return; // Validate origin if (event.data.type === "send-x") { console.log("Received x from parent:", event.data.value); } }); To send data to parent: In the parent page: window.addEventListener("message", (event) => { if (event.origin !== window.location.origin) return; // Optional for security if (event.data.type === "update") { window.someValue = event.data.newValue; console.log("Updated by iframe:", window.someValue); } }); In the iframe (e.g., on iframe.com): parent.postMessage({ type: "update", newValue: 6 }, "*"); ✅ Corrected Working Version Here’s a complete example to illustrate how to do it step by step using THREE.Clock and animation interpolation: 🔹 Step 1: Store the original position/rotation after loading Do this once, right after your model is placed in its starting orientation: const originalPosition = new THREE.Vector3(0, 0, 0); const originalRotation = new THREE.Euler(0, Math.PI / 4, 0); // 45° around Y ETSpinGroup.position.copy(originalPosition); ETSpinGroup.rotation.copy(originalRotation); 🔹 Step 2: Variables for animation state let resetting = false; let resetProgress = 0; const resetDuration = 1.0; // seconds let startPosition, startRotation; const clock = new THREE.Clock(); // needed for smooth time-based animation 🔹 Step 3: Handle the spin toggle button When the user enables spinning, start the reset animation first: document.getElementById('ETspinButton').addEventListener('click', () => { if (!ETspinEnabled) { // Save current state startPosition = ETSpinGroup.position.clone(); startRotation = ETSpinGroup.rotation.clone(); resetting = true; resetProgress = 0; clock.start(); // restart clock to get clean delta time } ETspinEnabled = true; }); 🔹 Step 4: Animate the reset in animate() In your animate() loop: function animate() { requestAnimationFrame(animate); const delta = clock.getDelta(); if (resetting) { resetProgress += delta / resetDuration; const t = Math.min(resetProgress, 1.0); // Interpolate position ETSpinGroup.position.lerpVectors(startPosition, originalPosition, t); // Interpolate rotation ETSpinGroup.rotation.x = THREE.MathUtils.lerp(startRotation.x, originalRotation.x, t); ETSpinGroup.rotation.y = THREE.MathUtils.lerp(startRotation.y, originalRotation.y, t); ETSpinGroup.rotation.z = THREE.MathUtils.lerp(startRotation.z, originalRotation.z, t); if (t >= 1.0) { resetting = false; cycle = 1; // start actual spin logic now } } else if (ETspinEnabled) { // Your existing spin logic ETSpinGroup.rotation.y += 0.01; // or however you spin it } controls.update(); renderer.render(scene, camera); }