canvas
.container
	.row.footer
		.col
			a(href='https://www.instagram.com/victorvergara.co/', target='_blank')
				img(src='https://victorvergara.co/logo.svg')
				
View Compiled
body
	margin: 0

canvas
	z-index: -1
	top: 0
	display: block
	width: 100%
	height: 100%
	position: fixed

.footer
	right: 2rem
	bottom: 2rem
	position: absolute
	img
		width: 2.5rem
		transition: opacity 0.5s
		&:hover
			opacity: 0.5
View Compiled
import * as THREE from "https://cdn.skypack.dev/three@0.140.0/build/three.module";
import { OrbitControls } from "https://threejsfundamentals.org/threejs/resources/threejs/r132/examples/jsm/controls/OrbitControls.js";
import { RoundedBoxGeometry } from "https://threejsfundamentals.org/threejs/resources/threejs/r132/examples/jsm/geometries/RoundedBoxGeometry.js";

const texture = {
	matcap:
		"https://images.unsplash.com/photo-1626908013943-df94de54984c?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2673&q=80",
	skin:
		"https://images.unsplash.com/photo-1560780552-ba54683cb263?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1740&q=80",
	env:
		"https://images.unsplash.com/photo-1536566482680-fca31930a0bd?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=987&q=80"
};

const config = {
	scene: {
		speed: 0.2
	},
	object: {
		speed: 0
	}
};

class Panel {
	constructor() {
		const pn = new Tweakpane.Pane({ title: "Panel" });
		const sn = pn.addFolder({ title: "Scene" });
		sn.addInput(config.scene, "speed", { min: 0, max: 1, label: "Speed" });
		const ob = pn.addFolder({ title: "Object" });
		ob.addInput(config.object, "speed", { min: 0, max: 1, label: "Speed" });
	}
}

class Control {
	constructor(props) {
		this.controls = new OrbitControls(props.camera, props.canvas);
		this.init();
	}
	init() {
		this.controls.target.set(0, 0, 0);
		this.controls.rotateSpeed = 0.9;
		this.controls.enableZoom = false;
		this.controls.enableDamping = true;
		this.controls.dampingFactor = 0.02;
		//this.controls.minPolarAngle = 1.8;
		//this.controls.maxPolarAngle = 1.8;
		this.update();
	}
	update() {
		this.controls.update();
	}
}

class LightBar {
	constructor(props) {
		this.geometry(props.scene, props.uid);
	}
	geometry(e, i) {
		const amp = 1;
		const c_mat = new THREE.MeshBasicMaterial();
		const c_geo = new THREE.CapsuleGeometry(0.02, 0.5 + Math.random(), 5, 16);
		this.c_mes = new THREE.Mesh(c_geo, c_mat);
		this.c_mes.position.y =
			-Math.random() * (amp / 2) + Math.random() * (amp / 2);
		this.c_mes.position.x = -Math.sin(i * 0.3) * Math.PI;
		this.c_mes.position.z = -Math.cos(i * 0.3) * Math.PI;
		e.add(this.c_mes);
	}
}

class Space {
	constructor(props) {
		this.name = props.name ? props.name : "Null";
		this.canvas = props.canvas ? props.canvas : null;
		this.main();
	}
	main() {
		this.renderer = new THREE.WebGLRenderer({
			canvas: this.canvas,
			antialias: true,
			alpha: true
		});
		this.clock = new THREE.Clock();
		this.scene = new THREE.Scene();
		this.camera = new THREE.PerspectiveCamera(35);
		//this.camera.position.set(0, -1.7, 5);
		this.camera.position.set(0, -1.7, 5);
		this.scene.background = new THREE.Color(0x000a0b);
		this.control = new Control({ camera: this.camera, canvas: this.canvas });
		//--
		this.axesHelper = new THREE.AxesHelper(2);
		this.axesHelper.position.y = -1.5;
		//this.scene.add(this.axesHelper);
		this.renderer.shadowMap.enabled = true;
		this.renderer.shadowMap.type = THREE.PCFShoftSHadowMap;
		this.init();
	}
	init() {
		//this.lights();
		this.object();
		this.capsule();
		this.render();
		this.loop();
		this.resize();
	}
	lights() {
		this.h_light = new THREE.HemisphereLight(0xffffff, 0xaaaacc, 1);
		this.p_light = new THREE.PointLight(0xffffff, 0.2);
		this.p_light.castShadow = true;
		this.p_light.position.set(1, 5, 1);
		this.scene.add(this.h_light, this.p_light);
	}
	capsule() {
		for (let i = 0; i <= 20; i++) {
			const lightbar = new LightBar({ scene: this.scene, uid: i });
		}
	}
	object() {
		const o_geo = new RoundedBoxGeometry(1, 1, 1, 5, 0.05);
		const c_geo = new THREE.CircleGeometry(5, 5);
		const o_mat = new THREE.MeshMatcapMaterial({
			color: 0xffffff,
			//side: THREE.DoubleSide,
			matcap: new THREE.TextureLoader().load(texture.matcap),
			map: new THREE.TextureLoader().load(texture.env)
		});

		this.c_mes = new THREE.Mesh(c_geo, o_mat);
		this.o_mes = new THREE.Mesh(o_geo, o_mat);
		this.c_mes.rotateX(Math.PI / 2);
		this.c_mes.position.y = -1;
		this.scene.add(this.o_mes);
	}
	resize() {
		this.camera.aspect = window.innerWidth / window.innerHeight;
		this.camera.updateProjectionMatrix();
		this.renderer.setSize(window.innerWidth, window.innerHeight);
	}
	render() {
		this.scene.rotation.y = this.clock.getElapsedTime() * config.scene.speed;
		this.o_mes.rotation.y = -this.clock.getElapsedTime() * config.object.speed;
		this.o_mes.rotation.z = this.clock.getElapsedTime() * config.object.speed;
		this.o_mes.rotation.x = this.clock.getElapsedTime() * config.object.speed;
		this.o_mes.position.y =
			Math.sin(this.clock.getElapsedTime() * config.object.speed) * 0.2;
		this.camera.lookAt(this.scene.position);
		this.camera.updateMatrixWorld();
		this.renderer.render(this.scene, this.camera);
		this.control.update();
	}
	loop() {
		this.render();
		requestAnimationFrame(this.loop.bind(this));
	}
}

const canvas = document.querySelector("canvas");
const world = new Space({ canvas });
const panel = new Panel();
window.addEventListener("resize", () => world.resize());
window.addEventListener("load", () => world.resize());
world.resize();

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.