├── .gitignore ├── Experience ├── Camera.js ├── Experience.js ├── Preloader.js ├── Renderer.js ├── Theme.js ├── Utils │ ├── Resources.js │ ├── Sizes.js │ ├── Time.js │ ├── assets.js │ └── covertDivsToSpans.js └── World │ ├── Controls.js │ ├── Environment.js │ ├── Floor.js │ ├── Room.js │ └── World.js ├── LEARN.md ├── LICENSE.md ├── README.md ├── favicon.svg ├── index.html ├── main.js ├── package-lock.json ├── package.json ├── public ├── draco │ ├── README.md │ ├── draco_decoder.js │ ├── draco_decoder.wasm │ ├── draco_encoder.js │ ├── draco_wasm_wrapper.js │ └── gltf │ │ ├── draco_decoder.js │ │ ├── draco_decoder.wasm │ │ ├── draco_encoder.js │ │ └── draco_wasm_wrapper.js ├── models │ ├── Finale Version 16.glb │ └── New Boko.glb ├── social │ ├── credly.jpg │ ├── credly.pdf │ └── screenshot.png └── textures │ └── kda.mp4 └── style.css /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /Experience/Camera.js: -------------------------------------------------------------------------------- 1 | import * as THREE from "three"; 2 | import Experience from "./Experience.js"; 3 | import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js"; 4 | 5 | export default class Camera { 6 | constructor() { 7 | this.experience = new Experience(); 8 | this.sizes = this.experience.sizes; 9 | this.scene = this.experience.scene; 10 | this.canvas = this.experience.canvas; 11 | 12 | this.createPerspectiveCamera(); 13 | this.createOrthographicCamera(); 14 | this.setOrbitControls(); 15 | } 16 | 17 | createPerspectiveCamera() { 18 | this.perspectiveCamera = new THREE.PerspectiveCamera( 19 | 35, 20 | this.sizes.aspect, 21 | 0.1, 22 | 1000 23 | ); 24 | this.scene.add(this.perspectiveCamera); 25 | this.perspectiveCamera.position.x = 29; 26 | this.perspectiveCamera.position.y = 14; 27 | this.perspectiveCamera.position.z = 12; 28 | } 29 | 30 | createOrthographicCamera() { 31 | this.orthographicCamera = new THREE.OrthographicCamera( 32 | (-this.sizes.aspect * this.sizes.frustrum) / 2, 33 | (this.sizes.aspect * this.sizes.frustrum) / 2, 34 | this.sizes.frustrum / 2, 35 | -this.sizes.frustrum / 2, 36 | -50, 37 | 50 38 | ); 39 | 40 | // 6.5 41 | this.orthographicCamera.position.y = 5.65; 42 | this.orthographicCamera.position.z = 10; 43 | this.orthographicCamera.rotation.x = -Math.PI / 6; 44 | 45 | this.scene.add(this.orthographicCamera); 46 | 47 | // this.helper = new THREE.CameraHelper(this.orthographicCamera); 48 | // this.scene.add(this.helper); 49 | 50 | const size = 20; 51 | const divisions = 20; 52 | 53 | // const gridHelper = new THREE.GridHelper(size, divisions); 54 | // this.scene.add(gridHelper); 55 | 56 | // const axesHelper = new THREE.AxesHelper(10); 57 | // this.scene.add(axesHelper); 58 | } 59 | 60 | setOrbitControls() { 61 | this.controls = new OrbitControls(this.perspectiveCamera, this.canvas); 62 | this.controls.enableDamping = true; 63 | this.controls.enableZoom = false; 64 | } 65 | 66 | resize() { 67 | // Updating Perspective Camera on Resize 68 | this.perspectiveCamera.aspect = this.sizes.aspect; 69 | this.perspectiveCamera.updateProjectionMatrix(); 70 | 71 | // Updating Orthographic Camera on Resize 72 | this.orthographicCamera.left = 73 | (-this.sizes.aspect * this.sizes.frustrum) / 2; 74 | this.orthographicCamera.right = 75 | (this.sizes.aspect * this.sizes.frustrum) / 2; 76 | this.orthographicCamera.top = this.sizes.frustrum / 2; 77 | this.orthographicCamera.bottom = -this.sizes.frustrum / 2; 78 | this.orthographicCamera.updateProjectionMatrix(); 79 | } 80 | 81 | update() { 82 | // console.log(this.perspectiveCamera.position); 83 | this.controls.update(); 84 | 85 | // this.helper.matrixWorldNeedsUpdate = true; 86 | // this.helper.update(); 87 | // this.helper.position.copy(this.orthographicCamera.position); 88 | // this.helper.rotation.copy(this.orthographicCamera.rotation); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Experience/Experience.js: -------------------------------------------------------------------------------- 1 | import * as THREE from "three"; 2 | 3 | import Sizes from "./Utils/Sizes.js"; 4 | import Time from "./Utils/Time.js"; 5 | import Resources from "./Utils/Resources.js"; 6 | import assets from "./Utils/assets.js"; 7 | 8 | import Camera from "./Camera.js"; 9 | import Theme from "./Theme.js"; 10 | import Renderer from "./Renderer.js"; 11 | import Preloader from "./Preloader.js"; 12 | 13 | import World from "./World/World.js"; 14 | import Controls from "./World/Controls.js"; 15 | 16 | export default class Experience { 17 | static instance; 18 | constructor(canvas) { 19 | if (Experience.instance) { 20 | return Experience.instance; 21 | } 22 | Experience.instance = this; 23 | this.canvas = canvas; 24 | this.scene = new THREE.Scene(); 25 | this.time = new Time(); 26 | this.sizes = new Sizes(); 27 | this.camera = new Camera(); 28 | this.renderer = new Renderer(); 29 | this.resources = new Resources(assets); 30 | this.theme = new Theme(); 31 | this.world = new World(); 32 | this.preloader = new Preloader(); 33 | 34 | this.preloader.on("enablecontrols", () => { 35 | this.controls = new Controls(); 36 | }); 37 | 38 | this.sizes.on("resize", () => { 39 | this.resize(); 40 | }); 41 | this.time.on("update", () => { 42 | this.update(); 43 | }); 44 | } 45 | 46 | resize() { 47 | this.camera.resize(); 48 | this.world.resize(); 49 | this.renderer.resize(); 50 | } 51 | 52 | update() { 53 | this.preloader.update(); 54 | this.camera.update(); 55 | this.world.update(); 56 | this.renderer.update(); 57 | if (this.controls) { 58 | this.controls.update(); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Experience/Preloader.js: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from "events"; 2 | import Experience from "./Experience.js"; 3 | import GSAP from "gsap"; 4 | import convert from "./Utils/covertDivsToSpans.js"; 5 | 6 | export default class Preloader extends EventEmitter { 7 | constructor() { 8 | super(); 9 | this.experience = new Experience(); 10 | this.scene = this.experience.scene; 11 | this.sizes = this.experience.sizes; 12 | this.resources = this.experience.resources; 13 | this.camera = this.experience.camera; 14 | this.world = this.experience.world; 15 | this.device = this.sizes.device; 16 | 17 | this.sizes.on("switchdevice", (device) => { 18 | this.device = device; 19 | }); 20 | 21 | this.world.on("worldready", () => { 22 | this.setAssets(); 23 | this.playIntro(); 24 | }); 25 | } 26 | 27 | setAssets() { 28 | convert(document.querySelector(".intro-text")); 29 | convert(document.querySelector(".hero-main-title")); 30 | convert(document.querySelector(".hero-main-description")); 31 | convert(document.querySelector(".hero-second-subheading")); 32 | convert(document.querySelector(".second-sub")); 33 | 34 | this.room = this.experience.world.room.actualRoom; 35 | this.roomChildren = this.experience.world.room.roomChildren; 36 | console.log(this.roomChildren); 37 | } 38 | 39 | firstIntro() { 40 | return new Promise((resolve) => { 41 | this.timeline = new GSAP.timeline(); 42 | this.timeline.set(".animatedis", { y: 0, yPercent: 100 }); 43 | this.timeline.to(".preloader", { 44 | opacity: 0, 45 | delay: 1, 46 | onComplete: () => { 47 | document 48 | .querySelector(".preloader") 49 | .classList.add("hidden"); 50 | }, 51 | }); 52 | if (this.device === "desktop") { 53 | this.timeline 54 | .to(this.roomChildren.cube.scale, { 55 | x: 1.4, 56 | y: 1.4, 57 | z: 1.4, 58 | ease: "back.out(2.5)", 59 | duration: 0.7, 60 | }) 61 | .to(this.room.position, { 62 | x: -1, 63 | ease: "power1.out", 64 | duration: 0.7, 65 | }); 66 | } else { 67 | this.timeline 68 | .to(this.roomChildren.cube.scale, { 69 | x: 1.4, 70 | y: 1.4, 71 | z: 1.4, 72 | ease: "back.out(2.5)", 73 | duration: 0.7, 74 | }) 75 | .to(this.room.position, { 76 | z: -1, 77 | ease: "power1.out", 78 | duration: 0.7, 79 | }); 80 | } 81 | this.timeline 82 | .to(".intro-text .animatedis", { 83 | yPercent: 0, 84 | stagger: 0.05, 85 | ease: "back.out(1.7)", 86 | }) 87 | .to( 88 | ".arrow-svg-wrapper", 89 | { 90 | opacity: 1, 91 | }, 92 | "same" 93 | ) 94 | .to( 95 | ".toggle-bar", 96 | { 97 | opacity: 1, 98 | onComplete: resolve, 99 | }, 100 | "same" 101 | ); 102 | }); 103 | } 104 | 105 | secondIntro() { 106 | return new Promise((resolve) => { 107 | this.secondTimeline = new GSAP.timeline(); 108 | 109 | this.secondTimeline 110 | .to( 111 | ".intro-text .animatedis", 112 | { 113 | yPercent: 100, 114 | stagger: 0.05, 115 | ease: "back.in(1.7)", 116 | }, 117 | "fadeout" 118 | ) 119 | .to( 120 | ".arrow-svg-wrapper", 121 | { 122 | opacity: 0, 123 | }, 124 | "fadeout" 125 | ) 126 | .to( 127 | this.room.position, 128 | { 129 | x: 0, 130 | y: 0, 131 | z: 0, 132 | ease: "power1.out", 133 | }, 134 | "same" 135 | ) 136 | .to( 137 | this.roomChildren.cube.rotation, 138 | { 139 | y: 2 * Math.PI + Math.PI / 4, 140 | }, 141 | "same" 142 | ) 143 | .to( 144 | this.roomChildren.cube.scale, 145 | { 146 | x: 10, 147 | y: 10, 148 | z: 10, 149 | }, 150 | "same" 151 | ) 152 | .to( 153 | this.camera.orthographicCamera.position, 154 | { 155 | y: 6.5, 156 | }, 157 | "same" 158 | ) 159 | .to( 160 | this.roomChildren.cube.position, 161 | { 162 | x: 0.638711, 163 | y: 8.5618, 164 | z: 1.3243, 165 | }, 166 | "same" 167 | ) 168 | .set(this.roomChildren.body.scale, { 169 | x: 1, 170 | y: 1, 171 | z: 1, 172 | }) 173 | .to( 174 | this.roomChildren.cube.scale, 175 | { 176 | x: 0, 177 | y: 0, 178 | z: 0, 179 | duration: 1, 180 | }, 181 | "introtext" 182 | ) 183 | .to( 184 | ".hero-main-title .animatedis", 185 | { 186 | yPercent: 0, 187 | stagger: 0.07, 188 | ease: "back.out(1.7)", 189 | }, 190 | "introtext" 191 | ) 192 | .to( 193 | ".hero-main-description .animatedis", 194 | { 195 | yPercent: 0, 196 | stagger: 0.07, 197 | ease: "back.out(1.7)", 198 | }, 199 | "introtext" 200 | ) 201 | .to( 202 | ".first-sub .animatedis", 203 | { 204 | yPercent: 0, 205 | stagger: 0.07, 206 | ease: "back.out(1.7)", 207 | }, 208 | "introtext" 209 | ) 210 | .to( 211 | ".second-sub .animatedis", 212 | { 213 | yPercent: 0, 214 | stagger: 0.07, 215 | ease: "back.out(1.7)", 216 | }, 217 | "introtext" 218 | ) 219 | .to( 220 | this.roomChildren.aquarium.scale, 221 | { 222 | x: 1, 223 | y: 1, 224 | z: 1, 225 | ease: "back.out(2.2)", 226 | duration: 0.5, 227 | }, 228 | ">-0.5" 229 | ) 230 | .to( 231 | this.roomChildren.clock.scale, 232 | { 233 | x: 1, 234 | y: 1, 235 | z: 1, 236 | ease: "back.out(2.2)", 237 | duration: 0.5, 238 | }, 239 | ">-0.4" 240 | ) 241 | .to( 242 | this.roomChildren.shelves.scale, 243 | { 244 | x: 1, 245 | y: 1, 246 | z: 1, 247 | ease: "back.out(2.2)", 248 | duration: 0.5, 249 | }, 250 | ">-0.3" 251 | ) 252 | .to( 253 | this.roomChildren.floor_items.scale, 254 | { 255 | x: 1, 256 | y: 1, 257 | z: 1, 258 | ease: "back.out(2.2)", 259 | duration: 0.5, 260 | }, 261 | ">-0.2" 262 | ) 263 | .to( 264 | this.roomChildren.desks.scale, 265 | { 266 | x: 1, 267 | y: 1, 268 | z: 1, 269 | ease: "back.out(2.2)", 270 | duration: 0.5, 271 | }, 272 | ">-0.1" 273 | ) 274 | .to( 275 | this.roomChildren.table_stuff.scale, 276 | { 277 | x: 1, 278 | y: 1, 279 | z: 1, 280 | ease: "back.out(2.2)", 281 | duration: 0.5, 282 | }, 283 | ">-0.1" 284 | ) 285 | .to(this.roomChildren.computer.scale, { 286 | x: 1, 287 | y: 1, 288 | z: 1, 289 | ease: "back.out(2.2)", 290 | duration: 0.5, 291 | }) 292 | .set(this.roomChildren.mini_floor.scale, { 293 | x: 1, 294 | y: 1, 295 | z: 1, 296 | }) 297 | .to( 298 | this.roomChildren.chair.scale, 299 | { 300 | x: 1, 301 | y: 1, 302 | z: 1, 303 | ease: "back.out(2.2)", 304 | duration: 0.5, 305 | }, 306 | "chair" 307 | ) 308 | .to( 309 | this.roomChildren.fish.scale, 310 | { 311 | x: 1, 312 | y: 1, 313 | z: 1, 314 | ease: "back.out(2.2)", 315 | duration: 0.5, 316 | }, 317 | "chair" 318 | ) 319 | .to( 320 | this.roomChildren.chair.rotation, 321 | { 322 | y: 4 * Math.PI + Math.PI / 4, 323 | ease: "power2.out", 324 | duration: 1, 325 | }, 326 | "chair" 327 | ) 328 | .to(".arrow-svg-wrapper", { 329 | opacity: 1, 330 | onComplete: resolve, 331 | }); 332 | }); 333 | } 334 | 335 | onScroll(e) { 336 | if (e.deltaY > 0) { 337 | this.removeEventListeners(); 338 | this.playSecondIntro(); 339 | } 340 | } 341 | 342 | onTouch(e) { 343 | this.initalY = e.touches[0].clientY; 344 | } 345 | 346 | onTouchMove(e) { 347 | let currentY = e.touches[0].clientY; 348 | let difference = this.initalY - currentY; 349 | if (difference > 0) { 350 | console.log("swipped up"); 351 | this.removeEventListeners(); 352 | this.playSecondIntro(); 353 | } 354 | this.intialY = null; 355 | } 356 | 357 | removeEventListeners() { 358 | window.removeEventListener("wheel", this.scrollOnceEvent); 359 | window.removeEventListener("touchstart", this.touchStart); 360 | window.removeEventListener("touchmove", this.touchMove); 361 | } 362 | 363 | async playIntro() { 364 | this.scaleFlag = true; 365 | await this.firstIntro(); 366 | this.moveFlag = true; 367 | this.scrollOnceEvent = this.onScroll.bind(this); 368 | this.touchStart = this.onTouch.bind(this); 369 | this.touchMove = this.onTouchMove.bind(this); 370 | window.addEventListener("wheel", this.scrollOnceEvent); 371 | window.addEventListener("touchstart", this.touchStart); 372 | window.addEventListener("touchmove", this.touchMove); 373 | } 374 | async playSecondIntro() { 375 | this.moveFlag = false; 376 | await this.secondIntro(); 377 | this.scaleFlag = false; 378 | this.emit("enablecontrols"); 379 | } 380 | 381 | move() { 382 | if (this.device === "desktop") { 383 | this.room.position.set(-1, 0, 0); 384 | } else { 385 | this.room.position.set(0, 0, -1); 386 | } 387 | } 388 | 389 | scale() { 390 | this.roomChildren.rectLight.width = 0; 391 | this.roomChildren.rectLight.height = 0; 392 | 393 | if (this.device === "desktop") { 394 | this.room.scale.set(0.11, 0.11, 0.11); 395 | } else { 396 | this.room.scale.set(0.07, 0.07, 0.07); 397 | } 398 | } 399 | 400 | update() { 401 | if (this.moveFlag) { 402 | this.move(); 403 | } 404 | 405 | if (this.scaleFlag) { 406 | this.scale(); 407 | } 408 | } 409 | } 410 | -------------------------------------------------------------------------------- /Experience/Renderer.js: -------------------------------------------------------------------------------- 1 | import * as THREE from "three"; 2 | import Experience from "./Experience.js"; 3 | 4 | export default class Renderer { 5 | constructor() { 6 | this.experience = new Experience(); 7 | this.sizes = this.experience.sizes; 8 | this.scene = this.experience.scene; 9 | this.canvas = this.experience.canvas; 10 | this.camera = this.experience.camera; 11 | 12 | this.setRenderer(); 13 | } 14 | 15 | setRenderer() { 16 | this.renderer = new THREE.WebGLRenderer({ 17 | canvas: this.canvas, 18 | antialias: true, 19 | }); 20 | 21 | this.renderer.physicallyCorrectLights = true; 22 | this.renderer.outputEncoding = THREE.sRGBEncoding; 23 | this.renderer.toneMapping = THREE.CineonToneMapping; 24 | this.renderer.toneMappingExposure = 1.75; 25 | this.renderer.shadowMap.enabled = true; 26 | this.renderer.shadowMap.type = THREE.PCFSoftShadowMap; 27 | this.renderer.setSize(this.sizes.width, this.sizes.height); 28 | this.renderer.setPixelRatio(this.sizes.pixelRatio); 29 | } 30 | 31 | resize() { 32 | this.renderer.setSize(this.sizes.width, this.sizes.height); 33 | this.renderer.setPixelRatio(this.sizes.pixelRatio); 34 | } 35 | 36 | update() { 37 | // this.renderer.setViewport(0, 0, this.sizes.width, this.sizes.height); 38 | this.renderer.render(this.scene, this.camera.orthographicCamera); 39 | // Second Screen 40 | // this.renderer.setScissorTest(true); 41 | // this.renderer.setViewport( 42 | // this.sizes.width - this.sizes.width / 3, 43 | // this.sizes.height - this.sizes.height / 3, 44 | // this.sizes.width / 3, 45 | // this.sizes.height / 3 46 | // ); 47 | 48 | // this.renderer.setScissor( 49 | // this.sizes.width - this.sizes.width / 3, 50 | // this.sizes.height - this.sizes.height / 3, 51 | // this.sizes.width / 3, 52 | // this.sizes.height / 3 53 | // ); 54 | 55 | // this.renderer.render(this.scene, this.camera.perspectiveCamera); 56 | 57 | // this.renderer.setScissorTest(false); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Experience/Theme.js: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from "events"; 2 | 3 | export default class Theme extends EventEmitter { 4 | constructor() { 5 | super(); 6 | 7 | this.theme = "light"; 8 | 9 | this.toggleButton = document.querySelector(".toggle-button"); 10 | this.toggleCircle = document.querySelector(".toggle-circle"); 11 | 12 | this.setEventListeners(); 13 | } 14 | 15 | setEventListeners() { 16 | this.toggleButton.addEventListener("click", () => { 17 | this.toggleCircle.classList.toggle("slide"); 18 | this.theme = this.theme === "light" ? "dark" : "light"; 19 | document.body.classList.toggle("dark-theme"); 20 | document.body.classList.toggle("light-theme"); 21 | // console.log(this.theme); 22 | 23 | this.emit("switch", this.theme); 24 | }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Experience/Utils/Resources.js: -------------------------------------------------------------------------------- 1 | import * as THREE from "three"; 2 | 3 | import { EventEmitter } from "events"; 4 | import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js"; 5 | import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader.js"; 6 | import Experience from "../Experience.js"; 7 | 8 | export default class Resources extends EventEmitter { 9 | constructor(assets) { 10 | super(); 11 | this.experience = new Experience(); 12 | this.renderer = this.experience.renderer; 13 | 14 | this.assets = assets; 15 | 16 | this.items = {}; 17 | this.queue = this.assets.length; 18 | this.loaded = 0; 19 | 20 | this.setLoaders(); 21 | this.startLoading(); 22 | } 23 | 24 | setLoaders() { 25 | this.loaders = {}; 26 | this.loaders.gltfLoader = new GLTFLoader(); 27 | this.loaders.dracoLoader = new DRACOLoader(); 28 | this.loaders.dracoLoader.setDecoderPath("/draco/"); 29 | this.loaders.gltfLoader.setDRACOLoader(this.loaders.dracoLoader); 30 | } 31 | startLoading() { 32 | for (const asset of this.assets) { 33 | if (asset.type === "glbModel") { 34 | this.loaders.gltfLoader.load(asset.path, (file) => { 35 | this.singleAssetLoaded(asset, file); 36 | }); 37 | } else if (asset.type === "videoTexture") { 38 | this.video = {}; 39 | this.videoTexture = {}; 40 | 41 | this.video[asset.name] = document.createElement("video"); 42 | this.video[asset.name].src = asset.path; 43 | this.video[asset.name].muted = true; 44 | this.video[asset.name].playsInline = true; 45 | this.video[asset.name].autoplay = true; 46 | this.video[asset.name].loop = true; 47 | this.video[asset.name].play(); 48 | 49 | this.videoTexture[asset.name] = new THREE.VideoTexture( 50 | this.video[asset.name] 51 | ); 52 | // this.videoTexture[asset.name].flipY = false; 53 | this.videoTexture[asset.name].minFilter = THREE.NearestFilter; 54 | this.videoTexture[asset.name].magFilter = THREE.NearestFilter; 55 | this.videoTexture[asset.name].generateMipmaps = false; 56 | this.videoTexture[asset.name].encoding = THREE.sRGBEncoding; 57 | 58 | this.singleAssetLoaded(asset, this.videoTexture[asset.name]); 59 | } 60 | } 61 | } 62 | 63 | singleAssetLoaded(asset, file) { 64 | this.items[asset.name] = file; 65 | this.loaded++; 66 | 67 | if (this.loaded === this.queue) { 68 | this.emit("ready"); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Experience/Utils/Sizes.js: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from "events"; 2 | 3 | export default class Sizes extends EventEmitter { 4 | constructor() { 5 | super(); 6 | this.width = window.innerWidth; 7 | this.height = window.innerHeight; 8 | this.aspect = this.width / this.height; 9 | this.pixelRatio = Math.min(window.devicePixelRatio, 2); 10 | this.frustrum = 5; 11 | if (this.width < 968) { 12 | this.device = "mobile"; 13 | } else { 14 | this.device = "desktop"; 15 | } 16 | 17 | window.addEventListener("resize", () => { 18 | this.width = window.innerWidth; 19 | this.height = window.innerHeight; 20 | this.aspect = this.width / this.height; 21 | this.pixelRatio = Math.min(window.devicePixelRatio, 2); 22 | this.emit("resize"); 23 | 24 | if (this.width < 968 && this.device !== "mobile") { 25 | this.device = "mobile"; 26 | this.emit("switchdevice", this.device); 27 | } else if (this.width >= 968 && this.device !== "desktop") { 28 | this.device = "desktop"; 29 | this.emit("switchdevice", this.device); 30 | } 31 | }); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Experience/Utils/Time.js: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from "events"; 2 | 3 | export default class Time extends EventEmitter { 4 | constructor() { 5 | super(); 6 | this.start = Date.now(); 7 | this.current = this.start; 8 | this.elapsed = 0; 9 | this.delta = 16; 10 | 11 | this.update(); 12 | } 13 | 14 | update() { 15 | const currentTime = Date.now(); 16 | this.delta = currentTime - this.current; 17 | this.current = currentTime; 18 | this.elapsed = this.current - this.start; 19 | 20 | this.emit("update"); 21 | window.requestAnimationFrame(() => this.update()); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Experience/Utils/assets.js: -------------------------------------------------------------------------------- 1 | export default [ 2 | { 3 | name: "room", 4 | type: "glbModel", 5 | path: "/models/Finale Version 16.glb", 6 | }, 7 | { 8 | name: "screen", 9 | type: "videoTexture", 10 | path: "/textures/kda.mp4", 11 | }, 12 | ]; 13 | -------------------------------------------------------------------------------- /Experience/Utils/covertDivsToSpans.js: -------------------------------------------------------------------------------- 1 | export default function (element) { 2 | element.style.overflow = "hidden"; 3 | element.innerHTML = element.innerText 4 | .split("") 5 | .map((char) => { 6 | if (char === " ") { 7 | return ` `; 8 | } 9 | return `${char}`; 10 | }) 11 | .join(""); 12 | 13 | return element; 14 | } 15 | -------------------------------------------------------------------------------- /Experience/World/Controls.js: -------------------------------------------------------------------------------- 1 | import * as THREE from "three"; 2 | import Experience from "../Experience.js"; 3 | import GSAP from "gsap"; 4 | import { ScrollTrigger } from "gsap/ScrollTrigger.js"; 5 | import ASScroll from "@ashthornton/asscroll"; 6 | 7 | export default class Controls { 8 | constructor() { 9 | this.experience = new Experience(); 10 | this.scene = this.experience.scene; 11 | this.sizes = this.experience.sizes; 12 | this.resources = this.experience.resources; 13 | this.time = this.experience.time; 14 | this.camera = this.experience.camera; 15 | this.room = this.experience.world.room.actualRoom; 16 | this.room.children.forEach((child) => { 17 | if (child.type === "RectAreaLight") { 18 | this.rectLight = child; 19 | } 20 | }); 21 | this.circleFirst = this.experience.world.floor.circleFirst; 22 | this.circleSecond = this.experience.world.floor.circleSecond; 23 | this.circleThird = this.experience.world.floor.circleThird; 24 | 25 | GSAP.registerPlugin(ScrollTrigger); 26 | 27 | document.querySelector(".page").style.overflow = "visible"; 28 | 29 | if ( 30 | !/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test( 31 | navigator.userAgent 32 | ) 33 | ) { 34 | this.setSmoothScroll(); 35 | } 36 | this.setScrollTrigger(); 37 | } 38 | 39 | setupASScroll() { 40 | // https://github.com/ashthornton/asscroll 41 | const asscroll = new ASScroll({ 42 | ease: 0.1, 43 | disableRaf: true, 44 | }); 45 | 46 | GSAP.ticker.add(asscroll.update); 47 | 48 | ScrollTrigger.defaults({ 49 | scroller: asscroll.containerElement, 50 | }); 51 | 52 | ScrollTrigger.scrollerProxy(asscroll.containerElement, { 53 | scrollTop(value) { 54 | if (arguments.length) { 55 | asscroll.currentPos = value; 56 | return; 57 | } 58 | return asscroll.currentPos; 59 | }, 60 | getBoundingClientRect() { 61 | return { 62 | top: 0, 63 | left: 0, 64 | width: window.innerWidth, 65 | height: window.innerHeight, 66 | }; 67 | }, 68 | fixedMarkers: true, 69 | }); 70 | 71 | asscroll.on("update", ScrollTrigger.update); 72 | ScrollTrigger.addEventListener("refresh", asscroll.resize); 73 | 74 | requestAnimationFrame(() => { 75 | asscroll.enable({ 76 | newScrollElements: document.querySelectorAll( 77 | ".gsap-marker-start, .gsap-marker-end, [asscroll]" 78 | ), 79 | }); 80 | }); 81 | return asscroll; 82 | } 83 | 84 | setSmoothScroll() { 85 | this.asscroll = this.setupASScroll(); 86 | } 87 | 88 | setScrollTrigger() { 89 | ScrollTrigger.matchMedia({ 90 | //Desktop 91 | "(min-width: 969px)": () => { 92 | // console.log("fired desktop"); 93 | 94 | this.room.scale.set(0.11, 0.11, 0.11); 95 | this.rectLight.width = 0.5; 96 | this.rectLight.height = 0.7; 97 | this.camera.orthographicCamera.position.set(0, 6.5, 10); 98 | this.room.position.set(0, 0, 0); 99 | // First section ----------------------------------------- 100 | this.firstMoveTimeline = new GSAP.timeline({ 101 | scrollTrigger: { 102 | trigger: ".first-move", 103 | start: "top top", 104 | end: "bottom bottom", 105 | scrub: 0.6, 106 | // markers: true, 107 | invalidateOnRefresh: true, 108 | }, 109 | }); 110 | this.firstMoveTimeline.fromTo( 111 | this.room.position, 112 | { x: 0, y: 0, z: 0 }, 113 | { 114 | x: () => { 115 | return this.sizes.width * 0.0014; 116 | }, 117 | } 118 | ); 119 | 120 | // Second section ----------------------------------------- 121 | this.secondMoveTimeline = new GSAP.timeline({ 122 | scrollTrigger: { 123 | trigger: ".second-move", 124 | start: "top top", 125 | end: "bottom bottom", 126 | scrub: 0.6, 127 | invalidateOnRefresh: true, 128 | }, 129 | }) 130 | .to( 131 | this.room.position, 132 | { 133 | x: () => { 134 | return 1; 135 | }, 136 | z: () => { 137 | return this.sizes.height * 0.0032; 138 | }, 139 | }, 140 | "same" 141 | ) 142 | .to( 143 | this.room.scale, 144 | { 145 | x: 0.4, 146 | y: 0.4, 147 | z: 0.4, 148 | }, 149 | "same" 150 | ) 151 | .to( 152 | this.rectLight, 153 | { 154 | width: 0.5 * 4, 155 | height: 0.7 * 4, 156 | }, 157 | "same" 158 | ); 159 | 160 | // Third section ----------------------------------------- 161 | this.thirdMoveTimeline = new GSAP.timeline({ 162 | scrollTrigger: { 163 | trigger: ".third-move", 164 | start: "top top", 165 | end: "bottom bottom", 166 | scrub: 0.6, 167 | invalidateOnRefresh: true, 168 | }, 169 | }).to(this.camera.orthographicCamera.position, { 170 | y: 1.5, 171 | x: -4.1, 172 | }); 173 | }, 174 | 175 | // Mobile 176 | "(max-width: 968px)": () => { 177 | // console.log("fired mobile"); 178 | 179 | // Resets 180 | this.room.scale.set(0.07, 0.07, 0.07); 181 | this.room.position.set(0, 0, 0); 182 | this.rectLight.width = 0.3; 183 | this.rectLight.height = 0.4; 184 | this.camera.orthographicCamera.position.set(0, 6.5, 10); 185 | 186 | // First section ----------------------------------------- 187 | this.firstMoveTimeline = new GSAP.timeline({ 188 | scrollTrigger: { 189 | trigger: ".first-move", 190 | start: "top top", 191 | end: "bottom bottom", 192 | scrub: 0.6, 193 | // invalidateOnRefresh: true, 194 | }, 195 | }).to(this.room.scale, { 196 | x: 0.1, 197 | y: 0.1, 198 | z: 0.1, 199 | }); 200 | 201 | // Second section ----------------------------------------- 202 | this.secondMoveTimeline = new GSAP.timeline({ 203 | scrollTrigger: { 204 | trigger: ".second-move", 205 | start: "top top", 206 | end: "bottom bottom", 207 | scrub: 0.6, 208 | invalidateOnRefresh: true, 209 | }, 210 | }) 211 | .to( 212 | this.room.scale, 213 | { 214 | x: 0.25, 215 | y: 0.25, 216 | z: 0.25, 217 | }, 218 | "same" 219 | ) 220 | .to( 221 | this.rectLight, 222 | { 223 | width: 0.3 * 3.4, 224 | height: 0.4 * 3.4, 225 | }, 226 | "same" 227 | ) 228 | .to( 229 | this.room.position, 230 | { 231 | x: 1.5, 232 | }, 233 | "same" 234 | ); 235 | 236 | // Third section ----------------------------------------- 237 | this.thirdMoveTimeline = new GSAP.timeline({ 238 | scrollTrigger: { 239 | trigger: ".third-move", 240 | start: "top top", 241 | end: "bottom bottom", 242 | scrub: 0.6, 243 | invalidateOnRefresh: true, 244 | }, 245 | }).to(this.room.position, { 246 | z: -4.5, 247 | }); 248 | }, 249 | 250 | // all 251 | all: () => { 252 | this.sections = document.querySelectorAll(".section"); 253 | this.sections.forEach((section) => { 254 | this.progressWrapper = 255 | section.querySelector(".progress-wrapper"); 256 | this.progressBar = section.querySelector(".progress-bar"); 257 | 258 | if (section.classList.contains("right")) { 259 | GSAP.to(section, { 260 | borderTopLeftRadius: 10, 261 | scrollTrigger: { 262 | trigger: section, 263 | start: "top bottom", 264 | end: "top top", 265 | scrub: 0.6, 266 | }, 267 | }); 268 | GSAP.to(section, { 269 | borderBottomLeftRadius: 700, 270 | scrollTrigger: { 271 | trigger: section, 272 | start: "bottom bottom", 273 | end: "bottom top", 274 | scrub: 0.6, 275 | }, 276 | }); 277 | } else { 278 | GSAP.to(section, { 279 | borderTopRightRadius: 10, 280 | scrollTrigger: { 281 | trigger: section, 282 | start: "top bottom", 283 | end: "top top", 284 | scrub: 0.6, 285 | }, 286 | }); 287 | GSAP.to(section, { 288 | borderBottomRightRadius: 700, 289 | scrollTrigger: { 290 | trigger: section, 291 | start: "bottom bottom", 292 | end: "bottom top", 293 | scrub: 0.6, 294 | }, 295 | }); 296 | } 297 | GSAP.from(this.progressBar, { 298 | scaleY: 0, 299 | scrollTrigger: { 300 | trigger: section, 301 | start: "top top", 302 | end: "bottom bottom", 303 | scrub: 0.4, 304 | pin: this.progressWrapper, 305 | pinSpacing: false, 306 | }, 307 | }); 308 | }); 309 | 310 | // All animations 311 | // First section ----------------------------------------- 312 | this.firstCircle = new GSAP.timeline({ 313 | scrollTrigger: { 314 | trigger: ".first-move", 315 | start: "top top", 316 | end: "bottom bottom", 317 | scrub: 0.6, 318 | }, 319 | }).to(this.circleFirst.scale, { 320 | x: 3, 321 | y: 3, 322 | z: 3, 323 | }); 324 | 325 | // Second section ----------------------------------------- 326 | this.secondCircle = new GSAP.timeline({ 327 | scrollTrigger: { 328 | trigger: ".second-move", 329 | start: "top top", 330 | end: "bottom bottom", 331 | scrub: 0.6, 332 | }, 333 | }) 334 | .to( 335 | this.circleSecond.scale, 336 | { 337 | x: 3, 338 | y: 3, 339 | z: 3, 340 | }, 341 | "same" 342 | ) 343 | .to( 344 | this.room.position, 345 | { 346 | y: 0.7, 347 | }, 348 | "same" 349 | ); 350 | 351 | // Third section ----------------------------------------- 352 | this.thirdCircle = new GSAP.timeline({ 353 | scrollTrigger: { 354 | trigger: ".third-move", 355 | start: "top top", 356 | end: "bottom bottom", 357 | scrub: 0.6, 358 | }, 359 | }).to(this.circleThird.scale, { 360 | x: 3, 361 | y: 3, 362 | z: 3, 363 | }); 364 | 365 | // Mini Platform Animations 366 | this.secondPartTimeline = new GSAP.timeline({ 367 | scrollTrigger: { 368 | trigger: ".third-move", 369 | start: "center center", 370 | }, 371 | }); 372 | 373 | this.room.children.forEach((child) => { 374 | if (child.name === "Mini_Floor") { 375 | this.first = GSAP.to(child.position, { 376 | x: -5.44055, 377 | z: 13.6135, 378 | duration: 0.3, 379 | }); 380 | } 381 | if (child.name === "Mailbox") { 382 | this.second = GSAP.to(child.scale, { 383 | x: 1, 384 | y: 1, 385 | z: 1, 386 | duration: 0.3, 387 | }); 388 | } 389 | if (child.name === "Lamp") { 390 | this.third = GSAP.to(child.scale, { 391 | x: 1, 392 | y: 1, 393 | z: 1, 394 | ease: "back.out(2)", 395 | duration: 0.3, 396 | }); 397 | } 398 | if (child.name === "FloorFirst") { 399 | this.fourth = GSAP.to(child.scale, { 400 | x: 1, 401 | y: 1, 402 | z: 1, 403 | ease: "back.out(2)", 404 | duration: 0.3, 405 | }); 406 | } 407 | if (child.name === "FloorSecond") { 408 | this.fifth = GSAP.to(child.scale, { 409 | x: 1, 410 | y: 1, 411 | z: 1, 412 | duration: 0.3, 413 | }); 414 | } 415 | if (child.name === "FloorThird") { 416 | this.sixth = GSAP.to(child.scale, { 417 | x: 1, 418 | y: 1, 419 | z: 1, 420 | ease: "back.out(2)", 421 | duration: 0.3, 422 | }); 423 | } 424 | if (child.name === "Dirt") { 425 | this.seventh = GSAP.to(child.scale, { 426 | x: 1, 427 | y: 1, 428 | z: 1, 429 | ease: "back.out(2)", 430 | duration: 0.3, 431 | }); 432 | } 433 | if (child.name === "Flower1") { 434 | this.eighth = GSAP.to(child.scale, { 435 | x: 1, 436 | y: 1, 437 | z: 1, 438 | ease: "back.out(2)", 439 | duration: 0.3, 440 | }); 441 | } 442 | if (child.name === "Flower2") { 443 | this.ninth = GSAP.to(child.scale, { 444 | x: 1, 445 | y: 1, 446 | z: 1, 447 | ease: "back.out(2)", 448 | duration: 0.3, 449 | }); 450 | } 451 | }); 452 | this.secondPartTimeline.add(this.first); 453 | this.secondPartTimeline.add(this.second); 454 | this.secondPartTimeline.add(this.third); 455 | this.secondPartTimeline.add(this.fourth, "-=0.2"); 456 | this.secondPartTimeline.add(this.fifth, "-=0.2"); 457 | this.secondPartTimeline.add(this.sixth, "-=0.2"); 458 | this.secondPartTimeline.add(this.seventh, "-=0.2"); 459 | this.secondPartTimeline.add(this.eighth); 460 | this.secondPartTimeline.add(this.ninth, "-=0.1"); 461 | }, 462 | }); 463 | } 464 | resize() {} 465 | 466 | update() {} 467 | } 468 | -------------------------------------------------------------------------------- /Experience/World/Environment.js: -------------------------------------------------------------------------------- 1 | import * as THREE from "three"; 2 | import Experience from "../Experience.js"; 3 | import GSAP from "gsap"; 4 | import GUI from "lil-gui"; 5 | 6 | export default class Environment { 7 | constructor() { 8 | this.experience = new Experience(); 9 | this.scene = this.experience.scene; 10 | 11 | // this.gui = new GUI({ container: document.querySelector(".hero-main") }); 12 | this.obj = { 13 | colorObj: { r: 0, g: 0, b: 0 }, 14 | intensity: 3, 15 | }; 16 | 17 | this.setSunlight(); 18 | // this.setGUI(); 19 | } 20 | 21 | setGUI() { 22 | this.gui.addColor(this.obj, "colorObj").onChange(() => { 23 | this.sunLight.color.copy(this.obj.colorObj); 24 | this.ambientLight.color.copy(this.obj.colorObj); 25 | console.log(this.obj.colorObj); 26 | }); 27 | this.gui.add(this.obj, "intensity", 0, 10).onChange(() => { 28 | this.sunLight.intensity = this.obj.intensity; 29 | this.sunLight.ambientLight = this.obj.intensity; 30 | }); 31 | } 32 | 33 | setSunlight() { 34 | this.sunLight = new THREE.DirectionalLight("#ffffff", 3); 35 | this.sunLight.castShadow = true; 36 | this.sunLight.shadow.camera.far = 20; 37 | this.sunLight.shadow.mapSize.set(2048, 2048); 38 | this.sunLight.shadow.normalBias = 0.05; 39 | // const helper = new THREE.CameraHelper(this.sunLight.shadow.camera); 40 | // this.scene.add(helper); 41 | 42 | this.sunLight.position.set(-1.5, 7, 3); 43 | this.scene.add(this.sunLight); 44 | 45 | this.ambientLight = new THREE.AmbientLight("#ffffff", 1); 46 | this.scene.add(this.ambientLight); 47 | } 48 | 49 | switchTheme(theme) { 50 | // console.log(this.sunLight); 51 | if (theme === "dark") { 52 | GSAP.to(this.sunLight.color, { 53 | r: 0.17254901960784313, 54 | g: 0.23137254901960785, 55 | b: 0.6862745098039216, 56 | }); 57 | GSAP.to(this.ambientLight.color, { 58 | r: 0.17254901960784313, 59 | g: 0.23137254901960785, 60 | b: 0.6862745098039216, 61 | }); 62 | GSAP.to(this.sunLight, { 63 | intensity: 0.78, 64 | }); 65 | GSAP.to(this.ambientLight, { 66 | intensity: 0.78, 67 | }); 68 | } else { 69 | GSAP.to(this.sunLight.color, { 70 | r: 255 / 255, 71 | g: 255 / 255, 72 | b: 255 / 255, 73 | }); 74 | GSAP.to(this.ambientLight.color, { 75 | r: 255 / 255, 76 | g: 255 / 255, 77 | b: 255 / 255, 78 | }); 79 | GSAP.to(this.sunLight, { 80 | intensity: 3, 81 | }); 82 | GSAP.to(this.ambientLight, { 83 | intensity: 1, 84 | }); 85 | } 86 | } 87 | 88 | resize() {} 89 | 90 | update() {} 91 | } 92 | -------------------------------------------------------------------------------- /Experience/World/Floor.js: -------------------------------------------------------------------------------- 1 | import * as THREE from "three"; 2 | import Experience from "../Experience.js"; 3 | 4 | export default class Floor { 5 | constructor() { 6 | this.experience = new Experience(); 7 | this.scene = this.experience.scene; 8 | 9 | this.setFloor(); 10 | this.setCircles(); 11 | } 12 | 13 | setFloor() { 14 | this.geometry = new THREE.PlaneGeometry(100, 100); 15 | this.material = new THREE.MeshStandardMaterial({ 16 | color: 0xffe6a2, 17 | side: THREE.BackSide, 18 | }); 19 | this.plane = new THREE.Mesh(this.geometry, this.material); 20 | this.scene.add(this.plane); 21 | this.plane.rotation.x = Math.PI / 2; 22 | this.plane.position.y = -0.3; 23 | this.plane.receiveShadow = true; 24 | } 25 | 26 | setCircles() { 27 | const geometry = new THREE.CircleGeometry(5, 64); 28 | const material = new THREE.MeshStandardMaterial({ color: 0xe5a1aa }); 29 | const material2 = new THREE.MeshStandardMaterial({ color: 0x8395cd }); 30 | const material3 = new THREE.MeshStandardMaterial({ color: 0x7ad0ac }); 31 | 32 | this.circleFirst = new THREE.Mesh(geometry, material); 33 | this.circleSecond = new THREE.Mesh(geometry, material2); 34 | this.circleThird = new THREE.Mesh(geometry, material3); 35 | 36 | this.circleFirst.position.y = -0.29; 37 | 38 | this.circleSecond.position.y = -0.28; 39 | this.circleSecond.position.x = 2; 40 | 41 | this.circleThird.position.y = -0.27; 42 | 43 | this.circleFirst.scale.set(0, 0, 0); 44 | this.circleSecond.scale.set(0, 0, 0); 45 | this.circleThird.scale.set(0, 0, 0); 46 | 47 | this.circleFirst.rotation.x = 48 | this.circleSecond.rotation.x = 49 | this.circleThird.rotation.x = 50 | -Math.PI / 2; 51 | 52 | this.circleFirst.receiveShadow = 53 | this.circleSecond.receiveShadow = 54 | this.circleThird.receiveShadow = 55 | true; 56 | 57 | this.scene.add(this.circleFirst); 58 | this.scene.add(this.circleSecond); 59 | this.scene.add(this.circleThird); 60 | } 61 | 62 | resize() {} 63 | 64 | update() {} 65 | } 66 | -------------------------------------------------------------------------------- /Experience/World/Room.js: -------------------------------------------------------------------------------- 1 | import * as THREE from "three"; 2 | import Experience from "../Experience.js"; 3 | import GSAP from "gsap"; 4 | import { RectAreaLightHelper } from "three/examples/jsm/helpers/RectAreaLightHelper.js"; 5 | 6 | export default class Room { 7 | constructor() { 8 | this.experience = new Experience(); 9 | this.scene = this.experience.scene; 10 | this.resources = this.experience.resources; 11 | this.time = this.experience.time; 12 | this.room = this.resources.items.room; 13 | this.actualRoom = this.room.scene; 14 | this.roomChildren = {}; 15 | 16 | this.lerp = { 17 | current: 0, 18 | target: 0, 19 | ease: 0.1, 20 | }; 21 | 22 | this.setModel(); 23 | this.setAnimation(); 24 | this.onMouseMove(); 25 | } 26 | 27 | setModel() { 28 | this.actualRoom.children.forEach((child) => { 29 | child.castShadow = true; 30 | child.receiveShadow = true; 31 | 32 | if (child instanceof THREE.Group) { 33 | child.children.forEach((groupchild) => { 34 | console.log(groupchild.material); 35 | groupchild.castShadow = true; 36 | groupchild.receiveShadow = true; 37 | }); 38 | } 39 | 40 | // console.log(child); 41 | 42 | if (child.name === "Aquarium") { 43 | // console.log(child); 44 | child.children[0].material = new THREE.MeshPhysicalMaterial(); 45 | child.children[0].material.roughness = 0; 46 | child.children[0].material.color.set(0x549dd2); 47 | child.children[0].material.ior = 3; 48 | child.children[0].material.transmission = 1; 49 | child.children[0].material.opacity = 1; 50 | child.children[0].material.depthWrite = false; 51 | child.children[0].material.depthTest = false; 52 | } 53 | 54 | if (child.name === "Computer") { 55 | child.children[1].material = new THREE.MeshBasicMaterial({ 56 | map: this.resources.items.screen, 57 | }); 58 | } 59 | 60 | if (child.name === "Mini_Floor") { 61 | child.position.x = -0.289521; 62 | child.position.z = 8.83572; 63 | } 64 | 65 | // if ( 66 | // child.name === "Mailbox" || 67 | // child.name === "Lamp" || 68 | // child.name === "FloorFirst" || 69 | // child.name === "FloorSecond" || 70 | // child.name === "FloorThird" || 71 | // child.name === "Dirt" || 72 | // child.name === "Flower1" || 73 | // child.name === "Flower2" 74 | // ) { 75 | // child.scale.set(0, 0, 0); 76 | // } 77 | 78 | child.scale.set(0, 0, 0); 79 | if (child.name === "Cube") { 80 | // child.scale.set(1, 1, 1); 81 | child.position.set(0, -1, 0); 82 | child.rotation.y = Math.PI / 4; 83 | } 84 | 85 | this.roomChildren[child.name.toLowerCase()] = child; 86 | }); 87 | 88 | const width = 0.5; 89 | const height = 0.7; 90 | const intensity = 1; 91 | const rectLight = new THREE.RectAreaLight( 92 | 0xffffff, 93 | intensity, 94 | width, 95 | height 96 | ); 97 | rectLight.position.set(7.68244, 7, 0.5); 98 | rectLight.rotation.x = -Math.PI / 2; 99 | rectLight.rotation.z = Math.PI / 4; 100 | this.actualRoom.add(rectLight); 101 | 102 | this.roomChildren["rectLight"] = rectLight; 103 | 104 | // const rectLightHelper = new RectAreaLightHelper(rectLight); 105 | // rectLight.add(rectLightHelper); 106 | // console.log(this.room); 107 | 108 | this.scene.add(this.actualRoom); 109 | this.actualRoom.scale.set(0.11, 0.11, 0.11); 110 | } 111 | 112 | setAnimation() { 113 | this.mixer = new THREE.AnimationMixer(this.actualRoom); 114 | this.swim = this.mixer.clipAction(this.room.animations[0]); 115 | this.swim.play(); 116 | } 117 | 118 | onMouseMove() { 119 | window.addEventListener("mousemove", (e) => { 120 | this.rotation = 121 | ((e.clientX - window.innerWidth / 2) * 2) / window.innerWidth; 122 | this.lerp.target = this.rotation * 0.05; 123 | }); 124 | } 125 | 126 | resize() {} 127 | 128 | update() { 129 | this.lerp.current = GSAP.utils.interpolate( 130 | this.lerp.current, 131 | this.lerp.target, 132 | this.lerp.ease 133 | ); 134 | 135 | this.actualRoom.rotation.y = this.lerp.current; 136 | 137 | this.mixer.update(this.time.delta * 0.0009); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /Experience/World/World.js: -------------------------------------------------------------------------------- 1 | import * as THREE from "three"; 2 | import Experience from "../Experience.js"; 3 | 4 | import Room from "./Room.js"; 5 | import Floor from "./Floor.js"; 6 | import Controls from "./Controls.js"; 7 | import Environment from "./Environment.js"; 8 | import { EventEmitter } from "events"; 9 | 10 | export default class World extends EventEmitter { 11 | constructor() { 12 | super(); 13 | this.experience = new Experience(); 14 | this.sizes = this.experience.sizes; 15 | this.scene = this.experience.scene; 16 | this.canvas = this.experience.canvas; 17 | this.camera = this.experience.camera; 18 | this.resources = this.experience.resources; 19 | this.theme = this.experience.theme; 20 | 21 | this.resources.on("ready", () => { 22 | this.environment = new Environment(); 23 | this.floor = new Floor(); 24 | this.room = new Room(); 25 | // this.controls = new Controls(); 26 | this.emit("worldready"); 27 | }); 28 | 29 | this.theme.on("switch", (theme) => { 30 | this.switchTheme(theme); 31 | }); 32 | 33 | // this.sizes.on("switchdevice", (device) => { 34 | // this.switchDevice(device); 35 | // }); 36 | } 37 | 38 | switchTheme(theme) { 39 | if (this.environment) { 40 | this.environment.switchTheme(theme); 41 | } 42 | } 43 | 44 | // switchDevice(device) { 45 | // if (this.controls) { 46 | // this.controls.switchDevice(device); 47 | // } 48 | // } 49 | 50 | resize() {} 51 | 52 | update() { 53 | if (this.room) { 54 | this.room.update(); 55 | } 56 | if (this.controls) { 57 | this.controls.update(); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /LEARN.md: -------------------------------------------------------------------------------- 1 | # 3D PORTFOLIO 2 | 3 | **[Live demo](https://mdamiruddin-3dportfolio.vercel.app)** 4 | 5 | This is the modification of **[Bokoko33's Portfolio](https://bokoko33.me/)** Website Code. 6 | 7 |  8 | 9 | ## For Setup 10 | 11 | ``` 12 | npm install 13 | npm run dev 14 | ``` 15 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Md Amiruddin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 3D PORTFOLIO 2 | 3 | **[Live demo](https://mdamiruddin-3dportfolio.vercel.app)** 4 | 5 | This is the modification of **[Bokoko33's Portfolio](https://bokoko33.me/)** Website Code. Please do not use this exact idea, 6 | but feel free to use it as a starting point/inspiration. 7 | 8 |  9 | 10 | ## For Setup 11 | 12 | ``` 13 | npm install 14 | npm run dev 15 | ``` 16 | 17 | 18 | -------------------------------------------------------------------------------- /favicon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 |Cyber Security Student
58 |Md Amiruddin
62 |Portfolio
63 |My name is Md Amiruddin and I'm currently learning from Hack the Box & TryHackMe. My passion lies in identifying, preventing, and resolving computer security issues and I am actively seeking internships or entry-level positions within the field.
89 |I have a deep foundational understanding of Web application penetration testing, classic encryption and cryptography, as well as operating system and network security such as intrusion detection/prevention systems. I am well-versed in not only the application of modern defense solutions, but also the development and testing of custom security protocols.
90 |I am always looking for an opportunity to demonstrate my knowledge of cyber security and to collaborate with others in the field. I am eager to make a contribution to any computer security initiatives, and I believe my technical expertise combined with my willingness to learn and act quickly make me an excellent candidate for any cyber-security related positions.
91 |I love creating CTF Machines and love doing Machines From HackTheBox!✌️.
92 |CGPA - 9.0
112 |[ 2020 - 2024 ]
114 |This project is a website dedicated to CTF (Capture The Flag) writeups. The website was designed with the goal of providing detailed and comprehensive analysis on the various challenges from a wide variety of CTF events.
137 |The website consists of a landing page which contains a list of all the CTF events with links to the write-ups. Each write-up contains a detailed review and explanation of the challenge, an overview of the techniques used to solve it, and a list of key points that were learned throughout the experience.
138 |This project is a web vulnerability analyzer written in Python. It aims to scan websites for potential security flaws and report them to the user. The program will first assess the target site for missing patches and outdated software, and then proceed to analyze any links associated with the page for malicious content.
146 |Once this task is complete, the program will then alert the user of any discovered vulnerabilities in a detailed report. The report will provide information on the discovered vulnerabilities, and suggest remedies to address the issue. Additionally, the program will allow users to set specific parameters and customize their security scans.
147 |This project is about the development of an automated chatbot for Whatsapp that can be used to manage appointments and orders for any store or service. The chatbot will be able to handle incoming messages, notify customers about availability, confirm and schedule appointments, take orders, process and keep track of issues.
152 |This powerful tool improves productivity and communication with customers and frees up business owners’ time to focus on other areas. The bot will be integrated with WhatsApp, allowing businesses to easily reach their customers and provide a superior customer experience. You can also add custom features to the bot for unique business needs.
153 |In this project, I create a Capture The Flag (CTF) challenge based on cryptography and hash cracking.
162 |I implemented a multitude of cryptography algorithms such as AES256-CBC, BASE-256, which had to be cracked or decrypted in order to reveal the flag. The challenge required the user to gain enough background knowledge on cryptography and hash cracking principles in order to solve the Flag.
163 |In this project, I created a Capture the Flag (CTF) challenge based on cryptography. The challenge involved a series of cryptography-related puzzles that had to be solved by players. The puzzles were designed to test the players’ understanding of core cryptographic concepts, such as hashing, symmetric and asymmetric encryption, digital signatures, and more.
170 |Additionally, the puzzles featured a variety of hash-cracking activities, including brute-force and dictionary attacks. The challenge was designed to increase players' familiarity with the fundamentals of cryptography. The puzzle-solving challenges were designed to be both fun and educational, so that players can learn cryptography by putting their skills to the test in a simulated CTF setting.
171 |Ethical Hacker Intern [ Oct 2022 - Dec 2022 ]
203 |Junior Cybersecurity Trainee [ Jan 2023 - Present ]
204 |• Performing Web Application Penetration Testing.
• Conducting Seminar at Colleges.
• Performing Black box, Grey box & White box Pentests.
• Creating reports, detailing assessment findings & blog writing.
If you'd like to get in touch, please feel free to connect with me on LinkedIn. On LinkedIn, I am active in discussions about network security, data privacy, and vulnerability management. I am available for interviews and I look forward to connecting with other professionals in the cybersecurity field.
245 |I am an aspiring cybersecurity student and avid tech enthusiast. I can be found on Github, where I participate in the open source community. I am always searching for different ways to learn and share knowledge pertaining to cyber security. I stay up to date with the newest technology, vulnerabilities, trends, and cyber security advancements. If you are looking to collaborate with someone who has a passion for learning and innovation, then you've come to the right place!
249 |I'm a cybersecurity enthusiast who enjoys writing CTF writeups on Medium. I share my experience, insights and lessons to help others learn. As I continue to work in this field, I aim to help build a global community where enthusiasts join forces and engage in meaningful conversations.
253 |