Frontend Forever App
We have a mobile app for you to download and use. And you can unlock many features in the app.
Get it now
Intall Later
Run
HTML
CSS
Javascript
Output
Document
Loading Flakes ❄️❄️❄️...
⤢
@charset "UTF-8"; @import url(https://fonts.googleapis.com/css?family=Nunito+Sans:300,400,600,700,800); *, :after, :before { box-sizing: border-box; padding: 0; margin: 0; } body { margin: 0; overflow: hidden; background: linear-gradient(to bottom, #0e1714, #0a0c0e); background-image: url("https://image.pollinations.ai/prompt/dark%20snowy%20night,%20high%20detail%20filters&model=flux&width=1800&height=1000"); background-size: cover; background-position: center; background-repeat: no-repeat; } canvas { width: 100vw; height: 100vh; opacity: 0.8; background: transparent; } .loading { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); color: white; font-family: Arial, sans-serif; } #fullscreen-btn { position: fixed; top: 20px; left: 20px; padding: 5px 10px 10px 10px; background: rgba(255, 255, 255, 0.2); border: none; color: white; cursor: pointer; border-radius: 5px; font-family: Arial, sans-serif; z-index: 1000; font-size: 20px; } #fullscreen-btn:hover { background: rgba(255, 255, 255, 0.3); }
console.log("Event Fired") const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 ); const renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); document.querySelector(".loading").style.display = "none"; const fullscreenBtn = document.getElementById("fullscreen-btn"); fullscreenBtn.addEventListener("click", () => { if (!document.fullscreenElement) { document.documentElement.requestFullscreen(); } else { document.exitFullscreen(); } }); function createSnowflakeGeometry() { const geometry = new THREE.BufferGeometry(); const vertices = []; for (let i = 0; i < 6; i++) { const angle = (i * Math.PI * 2) / 6; vertices.push( 0, 0, 0, Math.cos(angle) * 0.5, Math.sin(angle) * 0.5, 0, Math.cos(angle + Math.PI / 6) * 0.3, Math.sin(angle + Math.PI / 6) * 0.3, 0 ); } geometry.setAttribute( "position", new THREE.Float32BufferAttribute(vertices, 3) ); return geometry; } function createStarGeometry() { const geometry = new THREE.BufferGeometry(); const vertices = []; for (let i = 0; i < 5; i++) { const angle = (i * Math.PI * 2) / 5; const nextAngle = ((i + 1) * Math.PI * 2) / 5; vertices.push( 0, 0, 0, Math.cos(angle) * 0.5, Math.sin(angle) * 0.5, 0, Math.cos(nextAngle) * 0.5, Math.sin(nextAngle) * 0.5, 0 ); } geometry.setAttribute( "position", new THREE.Float32BufferAttribute(vertices, 3) ); return geometry; } function createTriangleGeometry() { const geometry = new THREE.BufferGeometry(); const vertices = [0, 0.5, 0, -0.5, -0.5, 0, 0.5, -0.5, 0]; geometry.setAttribute( "position", new THREE.Float32BufferAttribute(vertices, 3) ); return geometry; } function createCircleGeometry() { const geometry = new THREE.CircleGeometry(0.5, 32); return geometry; } function createSquareGeometry() { const geometry = new THREE.PlaneGeometry(1, 1); return geometry; } function createDonutGeometry() { const geometry = new THREE.TorusGeometry(0.5, 0.2, 16, 100); return geometry; } function createCubeGeometry() { const geometry = new THREE.BoxGeometry(1, 1, 1); return geometry; } function createPyramidGeometry() { const geometry = new THREE.ConeGeometry(0.5, 1, 4); return geometry; } function createSphereGeometry() { return new THREE.SphereGeometry(0.5, 32, 32); } function createEggGeometry() { const geometry = new THREE.SphereGeometry(0.5, 32, 32); geometry.scale(1, 1.5, 1); return geometry; } function createHexagonGeometry() { const geometry = new THREE.BufferGeometry(); const vertices = []; for (let i = 0; i < 6; i++) { const angle = (i * Math.PI * 2) / 6; const nextAngle = ((i + 1) * Math.PI * 2) / 6; vertices.push( 0, 0, 0, Math.cos(angle) * 0.5, Math.sin(angle) * 0.5, 0, Math.cos(nextAngle) * 0.5, Math.sin(nextAngle) * 0.5, 0 ); } geometry.setAttribute( "position", new THREE.Float32BufferAttribute(vertices, 3) ); return geometry; } function createBananaGeometry() { const curve = new THREE.CubicBezierCurve3( new THREE.Vector3(-0.5, 0, 0), new THREE.Vector3(-0.2, 0.5, 0), new THREE.Vector3(0.2, 0.5, 0), new THREE.Vector3(0.5, 0, 0) ); const geometry = new THREE.TubeGeometry(curve, 32, 0.1, 8, false); return geometry; } function getGeometryByShape(shape) { switch (shape) { case "banana": return createBananaGeometry(); case "circle": return createCircleGeometry(); case "cube": return createCubeGeometry(); case "donut": return createDonutGeometry(); case "egg": return createEggGeometry(); case "hexagon": return createHexagonGeometry(); case "pyramid": return createPyramidGeometry(); case "sphere": return createSphereGeometry(); case "square": return createSquareGeometry(); case "triangle": return createTriangleGeometry(); default: return createSnowflakeGeometry(); } } class Flake { constructor(shape = "snowflake") { this.geometry = getGeometryByShape(shape); this.material = new THREE.MeshBasicMaterial({ color: new THREE.Color().setHSL(Math.random(), 0.8, 0.6), side: THREE.DoubleSide, transparent: true, opacity: params.alpha }); this.mesh = new THREE.Mesh(this.geometry, this.material); this.orbitRadius = Math.random() * params.sizeDeviation + 2; this.orbitSpeed = ((Math.PI / 50) * (Math.random() * params.speedDeviation + 0.5)) / this.orbitRadius; this.orbitAngle = Math.random() * Math.PI * 2; this.mesh.rotation.x = Math.random() * Math.PI * 2; this.mesh.rotation.y = Math.random() * Math.PI * 2; this.mesh.rotation.z = Math.random() * Math.PI * 2; this.rotationSpeed = { x: (Math.random() - 0.5) * 0.02, y: (Math.random() - 0.5) * 0.02, z: (Math.random() - 0.5) * 0.02 }; scene.add(this.mesh); } update() { this.orbitAngle += this.orbitSpeed; this.mesh.position.x = Math.cos(this.orbitAngle) * this.orbitRadius; this.mesh.position.z = Math.sin(this.orbitAngle) * this.orbitRadius; this.mesh.position.y = Math.sin(this.orbitAngle * 0.5) * 2; this.mesh.rotation.x += this.rotationSpeed.x; this.mesh.rotation.y += this.rotationSpeed.y; this.mesh.rotation.z += this.rotationSpeed.z; this.material.color.setHSL( (Date.now() * params.colorChangeRate) % 1, 0.8, 0.6 ); this.material.opacity = params.alpha; } } let flakes = []; let params = { shape: "snowflake", alpha: 0.8, cameraAngle: 0, cameraSpeed: 1, cameraTilt: 0.77, cameraZoom: 8, colorChangeRate: 0.0001, flakeCount: 50, flakesSpeed: 1, sizeDeviation: 6, speedDeviation: 3, randomize: function () { params.alpha = Math.random() * 0.5 + 0.5; params.cameraAngle = Math.random() * Math.PI * 2; params.cameraSpeed = Math.random() * 1.9 + 0.1; params.cameraTilt = (Math.random() - 0.5) * Math.PI; params.cameraZoom = Math.random() * 25 + 5; params.colorChangeRate = Math.random() * 0.0002; params.flakesSpeed = Math.random() * 1.9 + 0.1; params.sizeDeviation = Math.random() * 10; params.speedDeviation = Math.random() * 5; updateFlakeCount(params.flakeCount); for (let i = 0; i < gui.__controllers.length; i++) { gui.__controllers[i].updateDisplay(); } }, reset: function () { params.alpha = 0.8; params.cameraAngle = 0; params.cameraSpeed = 1; params.cameraTilt = 0.77; params.cameraZoom = 8; params.colorChangeRate = 0.0001; params.flakesSpeed = 1; params.sizeDeviation = 6; params.speedDeviation = 3; params.shape = "snowflake"; updateFlakeCount(params.flakeCount); for (let i = 0; i < gui.__controllers.length; i++) { gui.__controllers[i].updateDisplay(); } } }; function updateFlakeCount(count, shape = params.shape) { flakes.forEach((flake) => scene.remove(flake.mesh)); flakes = []; for (let i = 0; i < count; i++) { flakes.push(new Flake(shape)); } } updateFlakeCount(50); camera.position.z = 8; const gui = new dat.GUI(); gui .add(params, "shape", [ "snowflake", "banana", "circle", "cube", "donut", "egg", "hexagon", "pyramid", "sphere", "square", "triangle" ]) .name("Shape") .onChange((value) => { updateFlakeCount(params.flakeCount, value); }); gui.add(params, "alpha", 0, 1, 0.01).name("Alpha"); gui.add(params, "cameraAngle", 0, Math.PI * 2, 0.01).name("Camera Angle"); gui.add(params, "cameraSpeed", 0.1, 2, 0.1).name("Camera Speed"); gui .add(params, "cameraTilt", -Math.PI / 2, Math.PI / 2, 0.01) .name("Camera Tilt"); gui.add(params, "cameraZoom", 5, 30, 1).name("Camera Zoom"); gui.add(params, "colorChangeRate", 0, 0.001, 0.00001).name("Color Change Rate"); gui .add(params, "flakeCount", 1, 100, 1) .name("Flake Count") .onChange((value) => { updateFlakeCount(value); }); gui.add(params, "flakesSpeed", 0.1, 2, 0.1).name("Flakes Speed"); gui.add(params, "sizeDeviation", 0, 10, 0.1).name("Size Deviation"); gui.add(params, "speedDeviation", 0, 5, 0.1).name("Speed Deviation"); gui.add(params, "randomize").name("Randomize"); gui.add(params, "reset").name("Reset"); function animate() { requestAnimationFrame(animate); flakes.forEach((flake) => flake.update()); const baseAngle = Date.now() * 0.0001 * params.cameraSpeed + params.cameraAngle; camera.position.x = Math.sin(baseAngle) * params.cameraZoom; camera.position.z = Math.cos(baseAngle) * params.cameraZoom; camera.position.y = Math.sin(params.cameraTilt) * params.cameraZoom; camera.lookAt(0, 0, 0); renderer.render(scene, camera); } window.addEventListener("resize", () => { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); }); animate();