3d model in a page using three.js

So if you follow along in the video, I refer to this blog post for you to either enter code at certain points into the HTML widget, so below is everything you will need!

The plugin to allow you to upload the model files into wordpress:

The image I used in the pre loader in the tutorial:

Sketchfab, the website to download your own models:

Models I used in the tutorial:

The Three.js script used to create the whole scene, model, camera, lights … just everything to perfectly handle the object.
This script is without camera controls, better for just displaying objects to the user.

				
					<div id="canvas-container-2" style="width: 100%; height: 100%; position: relative;max-height:100vh;">
    <div id="loading-screen" style="width: 100%; height: 90%; background-color: rgba(0,0,0,0.5); display: flex; justify-content: center; align-items: center; z-index: 1000;">
        <div style="text-align: center; color: white;">
            <img decoding="async" src="http://video-on-scroll.local/wp-content/uploads/2024/09/futuristic-tunnel-corridor-with-neon-glowing-light-2023-11-27-04-53-49-utc.jpeg" alt="Loading" style="max-width: 100%; max-height: 100%;">
            <p id="loading-progress">Loading 3D Model: 0%</p>
        </div>
    </div>
    <canvas id="cubeCanvas2" style="width: 100%; height: 100%;"></canvas>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/loaders/GLTFLoader.js"></script>

<script>
// ====== EASY TO MODIFY SETTINGS ======

// Model URL - Change this to your model's URL
const MODEL_URL = 'http://video-on-scroll.local/wp-content/uploads/2024/09/fresh_baked_bread.glb';

// Camera settings
const CAMERA_FOV = 50;
const CAMERA_NEAR = 0.1;
const CAMERA_FAR = 3000;
const CAMERA_DISTANCE_FACTOR_DESKTOP = 0.5; // Control for desktop
const CAMERA_DISTANCE_FACTOR_MOBILE = 0.8; // Control for mobile
const CAMERA_OFFSET_X = 0;
const CAMERA_OFFSET_Y = 0.3;

// Lighting settings
const DIRECTIONAL_LIGHT_COLOR = 0xffffff;
const DIRECTIONAL_LIGHT_INTENSITY = 0.2;
const AMBIENT_LIGHT_COLOR = 0xffffff;
const AMBIENT_LIGHT_INTENSITY = 5;
const ADDITIONAL_LIGHT_COLOR = 0xffffff;
const ADDITIONAL_LIGHT_INTENSITY = 0.5;

// Auto-rotation settings
const AUTO_ROTATE = true;
const AUTO_ROTATE_SPEED = 0.005;

// ====== SCENE SETUP ======

let canvas, renderer, scene, camera, light, ambientLight, additionalLight;
let model, rotationGroup, mixer, clock;

function setupScene() {
    canvas = document.getElementById('cubeCanvas2');
    renderer = new THREE.WebGLRenderer({ canvas: canvas, alpha: true, antialias: true });
    renderer.setClearColor(0x000000, 0);
    renderer.setPixelRatio(window.devicePixelRatio);

    scene = new THREE.Scene();
    camera = new THREE.PerspectiveCamera(CAMERA_FOV, canvas.clientWidth / canvas.clientHeight, CAMERA_NEAR, CAMERA_FAR);

    light = new THREE.DirectionalLight(DIRECTIONAL_LIGHT_COLOR, DIRECTIONAL_LIGHT_INTENSITY);
    light.position.set(1, 1, 1).normalize();
    scene.add(light);

    ambientLight = new THREE.AmbientLight(AMBIENT_LIGHT_COLOR, AMBIENT_LIGHT_INTENSITY);
    scene.add(ambientLight);

    additionalLight = new THREE.DirectionalLight(ADDITIONAL_LIGHT_COLOR, ADDITIONAL_LIGHT_INTENSITY);
    additionalLight.position.set(-1, -1, 5).normalize();
    scene.add(additionalLight);

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

    clock = new THREE.Clock();

    resizeCanvas();
}

function fitModelToCanvas() {
    if (!model) return;

    const box = new THREE.Box3().setFromObject(model);
    const center = box.getCenter(new THREE.Vector3());

    model.position.sub(center);

    // Set camera distance based on whether it's mobile or desktop
    const isMobile = window.innerWidth <= 768;
    let distance = isMobile ? CAMERA_DISTANCE_FACTOR_MOBILE : CAMERA_DISTANCE_FACTOR_DESKTOP;

    // Set camera position
    camera.position.set(CAMERA_OFFSET_X, CAMERA_OFFSET_Y, distance);

    camera.lookAt(new THREE.Vector3(0, 0, 0));

    // Update camera properties
    camera.updateProjectionMatrix();

    // Ensure the renderer fits the canvas size
    renderer.setSize(canvas.clientWidth, canvas.clientHeight);
}

function animate() {
    requestAnimationFrame(animate);

    if (mixer) {
        mixer.update(clock.getDelta());
    }

    if (AUTO_ROTATE) {
        rotationGroup.rotation.y += AUTO_ROTATE_SPEED;
    }

    renderer.render(scene, camera);
}

function resizeCanvas() {
    if (canvas && renderer && camera) {
        camera.aspect = canvas.clientWidth / canvas.clientHeight;
        camera.updateProjectionMatrix();

        renderer.setSize(canvas.clientWidth, canvas.clientHeight);

        if (model) {
            fitModelToCanvas();
        }
    }
}

function updateLoadingScreen(progress) {
    const progressText = document.getElementById('loading-progress');
    if (progressText) {
        progressText.textContent = `Loading 3D Model: ${Math.round(progress * 100)}%`;
    }
}

function hideLoadingScreen() {
    const loadingScreen = document.getElementById('loading-screen');
    if (loadingScreen) {
        loadingScreen.style.display = 'none';
    }
}

function startModelLoad() {
    const loadingManager = new THREE.LoadingManager();

    loadingManager.onProgress = function (url, itemsLoaded, itemsTotal) {
        updateLoadingScreen(itemsLoaded / itemsTotal);
    };

    loadingManager.onLoad = function () {
        hideLoadingScreen();
    };

    const loader = new THREE.GLTFLoader(loadingManager);

    loader.load(MODEL_URL, 
        function (gltf) {
            model = gltf.scene;
            rotationGroup.add(model);

            fitModelToCanvas();

            if (gltf.animations && gltf.animations.length) {
                mixer = new THREE.AnimationMixer(model);
                const animation = gltf.animations[0];
                const action = mixer.clipAction(animation);
                action.play();
            }

            animate(); // Start the animation loop
        },
        null, // Removed the console log for loading progress
        function (error) {
            console.error('An error occurred while loading the model:', error);
        }
    );
}

function initiateBackgroundLoading() {
    setupScene();
    startModelLoad();
}

// Initialize everything
initiateBackgroundLoading();
window.addEventListener('resize', resizeCanvas);
</script>

				
			

The Three.js script used to create the whole scene, model, camera, lights … just everything to perfectly handle the object.
This script is without camera controls, better for just displaying objects to the user.

				
					<div id="canvas-container-2" style="width: 100%; height: 100%; position: relative;max-height:100vh;">
    <div id="loading-screen" style="width: 100%; height: 90%; background-color: rgba(0,0,0,0.5); display: flex; justify-content: center; align-items: center; z-index: 1000;">
        <div style="text-align: center; color: white;">
            <img decoding="async" src="http://video-on-scroll.local/wp-content/uploads/2024/09/futuristic-tunnel-corridor-with-neon-glowing-light-2023-11-27-04-53-49-utc.jpeg" alt="Loading" style="max-width: 100%; max-height: 100%;">
            <p id="loading-progress">Loading 3D Model: 0%</p>
        </div>
    </div>
    <canvas id="cubeCanvas2" style="width: 100%; height: 100%;"></canvas>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/loaders/GLTFLoader.js"></script>

<script>
// ====== EASY TO MODIFY SETTINGS ======

// Model URL - Change this to your model's URL
const MODEL_URL = 'http://video-on-scroll.local/wp-content/uploads/2024/09/fresh_baked_bread.glb';

// Camera settings
const CAMERA_FOV = 50;
const CAMERA_NEAR = 0.1;
const CAMERA_FAR = 3000;
const CAMERA_DISTANCE_FACTOR_DESKTOP = 0.5; // Control for desktop
const CAMERA_DISTANCE_FACTOR_MOBILE = 0.8; // Control for mobile
const CAMERA_OFFSET_X = 0;
const CAMERA_OFFSET_Y = 0.3;

// Lighting settings
const DIRECTIONAL_LIGHT_COLOR = 0xffffff;
const DIRECTIONAL_LIGHT_INTENSITY = 0.2;
const AMBIENT_LIGHT_COLOR = 0xffffff;
const AMBIENT_LIGHT_INTENSITY = 5;
const ADDITIONAL_LIGHT_COLOR = 0xffffff;
const ADDITIONAL_LIGHT_INTENSITY = 0.5;

// Auto-rotation settings
const AUTO_ROTATE = true;
const AUTO_ROTATE_SPEED = 0.005;

// ====== SCENE SETUP ======

let canvas, renderer, scene, camera, light, ambientLight, additionalLight;
let model, rotationGroup, mixer, clock;

// Track initial camera distance to ensure zooming starts from this position
let initialCameraZ = null; 

function setupScene() {
    canvas = document.getElementById('cubeCanvas2');
    renderer = new THREE.WebGLRenderer({ canvas: canvas, alpha: true, antialias: true });
    renderer.setClearColor(0x000000, 0);
    renderer.setPixelRatio(window.devicePixelRatio);

    // Prevent context menu on right-click
    canvas.addEventListener('contextmenu', (e) => e.preventDefault());

    scene = new THREE.Scene();
    camera = new THREE.PerspectiveCamera(CAMERA_FOV, canvas.clientWidth / canvas.clientHeight, CAMERA_NEAR, CAMERA_FAR);

    // Setup lighting
    light = new THREE.DirectionalLight(DIRECTIONAL_LIGHT_COLOR, DIRECTIONAL_LIGHT_INTENSITY);
    light.position.set(1, 1, 1).normalize();
    scene.add(light);

    ambientLight = new THREE.AmbientLight(AMBIENT_LIGHT_COLOR, AMBIENT_LIGHT_INTENSITY);
    scene.add(ambientLight);

    additionalLight = new THREE.DirectionalLight(ADDITIONAL_LIGHT_COLOR, ADDITIONAL_LIGHT_INTENSITY);
    additionalLight.position.set(-1, -1, 5).normalize();
    scene.add(additionalLight);

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

    clock = new THREE.Clock();

    resizeCanvas();
}


function fitModelToCanvas() {
    if (!model) return;

    const box = new THREE.Box3().setFromObject(model);
    const center = box.getCenter(new THREE.Vector3());

    model.position.sub(center);

    // Set camera distance based on whether it's mobile or desktop
    const isMobile = window.innerWidth <= 768;
    let distance = isMobile ? CAMERA_DISTANCE_FACTOR_MOBILE : CAMERA_DISTANCE_FACTOR_DESKTOP;

    // Set camera position
    camera.position.set(CAMERA_OFFSET_X, CAMERA_OFFSET_Y, distance);

    // Store the initial camera Z position to control zoom properly
    if (initialCameraZ === null) {
        initialCameraZ = camera.position.z;  // Store this once during setup
    }

    camera.lookAt(new THREE.Vector3(0, 0, 0));

    // Update camera properties
    camera.updateProjectionMatrix();

    // Ensure the renderer fits the canvas size
    renderer.setSize(canvas.clientWidth, canvas.clientHeight);
}

function animate() {
    requestAnimationFrame(animate);

    if (mixer) {
        mixer.update(clock.getDelta());
    }

    if (AUTO_ROTATE) {
        rotationGroup.rotation.y += AUTO_ROTATE_SPEED;
    }

    renderer.render(scene, camera);
}

function resizeCanvas() {
    if (canvas && renderer && camera) {
        camera.aspect = canvas.clientWidth / canvas.clientHeight;
        camera.updateProjectionMatrix();

        renderer.setSize(canvas.clientWidth, canvas.clientHeight);

        if (model) {
            fitModelToCanvas();
        }
    }
}

function updateLoadingScreen(progress) {
    const progressText = document.getElementById('loading-progress');
    if (progressText) {
        progressText.textContent = `Loading 3D Model: ${Math.round(progress * 100)}%`;
    }
}

function hideLoadingScreen() {
    const loadingScreen = document.getElementById('loading-screen');
    if (loadingScreen) {
        loadingScreen.style.display = 'none';
    }
}

function startModelLoad() {
    const loadingManager = new THREE.LoadingManager();

    loadingManager.onProgress = function (url, itemsLoaded, itemsTotal) {
        updateLoadingScreen(itemsLoaded / itemsTotal);
    };

    loadingManager.onLoad = function () {
        hideLoadingScreen();
    };

    const loader = new THREE.GLTFLoader(loadingManager);

    loader.load(MODEL_URL, 
        function (gltf) {
            model = gltf.scene;
            rotationGroup.add(model);

            fitModelToCanvas();

            if (gltf.animations && gltf.animations.length) {
                mixer = new THREE.AnimationMixer(model);
                const animation = gltf.animations[0];
                const action = mixer.clipAction(animation);
                action.play();
            }

            animate(); // Start the animation loop
        },
        null, // Removed the console log for loading progress
        function (error) {
            console.error('An error occurred while loading the model:', error);
        }
    );
}

function initiateBackgroundLoading() {
    setupScene();
    startModelLoad();
}

let isDragging = false;
let isRightClick = false; // Flag for right-click movement
let previousMousePosition = { x: 0, y: 0 };

function onPointerDown(event) {
    isDragging = true;
    autoRotate = false; // Stop auto-rotation when dragging starts
    previousMousePosition = { x: event.clientX, y: event.clientY };

    if (event.button === 2) {
        isRightClick = true; // Right-click detected
    }
}

// Handle mouse movement (left-click for rotation, right-click for camera movement)
function onPointerMove(event) {
    if (!isDragging) return;

    const deltaMove = {
        x: event.clientX - previousMousePosition.x,
        y: event.clientY - previousMousePosition.y
    };

    const rotationSpeed = 0.005;
    const cameraMoveSpeed = 0.001;

    if (isRightClick) {
        // Right-click: Move camera horizontally (X axis) and vertically (Y axis)
        camera.position.x -= deltaMove.x * cameraMoveSpeed;
        camera.position.y += deltaMove.y * cameraMoveSpeed;  // Moving on Y-axis as before
    } else {
        // Left-click: Rotate the model
        rotationGroup.rotation.y += deltaMove.x * rotationSpeed;
        rotationGroup.rotation.x += deltaMove.y * rotationSpeed;
        rotationGroup.rotation.x = Math.max(-Math.PI / 2, Math.min(Math.PI / 2, rotationGroup.rotation.x)); // Limit vertical rotation
    }

    previousMousePosition = { x: event.clientX, y: event.clientY };
}

function onPointerUp() {
    isDragging = false;
    isRightClick = false; // Reset right-click flag
    autoRotate = true; // Resume auto-rotation when dragging stops
}
function zoom(event) {
    event.preventDefault();

    const zoomSpeed = 0.001;  // Slow down zoom for better control

    // Only modify the Z position from the initial distance, keeping the limits
    let newCameraZ = camera.position.z + event.deltaY * zoomSpeed;

    // Set zoom limits based on the initial camera position
    const minZoom = initialCameraZ * 0.01;  // You can tweak this value for the minimum zoom
    const maxZoom = initialCameraZ * 20;    // You can tweak this value for the maximum zoom

    // Apply the limits to the new Z position
    camera.position.z = Math.max(minZoom, Math.min(maxZoom, newCameraZ));

    camera.lookAt(new THREE.Vector3(0, 0, 0));  // Ensure the camera always focuses on the model center
    camera.updateProjectionMatrix();  // Update camera projection after zooming
}

// Initialize everything
initiateBackgroundLoading();
window.addEventListener('resize', resizeCanvas);
canvas.addEventListener('pointerdown', onPointerDown);
window.addEventListener('pointermove', onPointerMove);
window.addEventListener('pointerup', onPointerUp);
canvas.addEventListener('wheel', zoom);  // Add the zoom event listener

</script>

				
			

Other Articles

Building Mr Beast’s Feastables Website In WordPress | GSAP Elementor Tutorial

So if you follow along in the video, I refer to this blog post for you to enter code at ...

Building Peter McKinnon’s Website In WordPress

All Images used for the website tutorial: Download Images Please guys, these images are just to help copy the style ...

Never Seen Before – Animated SVG mask entrance effect for images in elementor

https://youtu.be/VduaRoRBuzM So if you follow along in the video, I refer to this blog post for you to enter code ...

GSAP Button Magic Elevate Your Elementor Button Hover Effects

https://youtu.be/CXxqXMzj4_k So if you follow along in the video, I refer to this blog post for you to enter code ...