<div id="app">
  <canvas id="canvas"></canvas>
</div>
body {
  margin: 0;
}

#app {
  background-image: radial-gradient( circle farthest-corner at 83.7% 4.3%,  rgba(35,10,45,1) 0%, rgba(10,10,10,1) 90% );
  width: 100vw;
  height: 100vh;
}

canvas {
  position: fixed;
  left: 0;
  top: 0;
}
import * as THREE from "https://threejs.org/build/three.module.js";
import Stats from 'https://threejs.org/examples/jsm/libs/stats.module.js';
import anime from "https://cdn.skypack.dev/animejs@3.2.1";



const PARTICLE_SIZE = 500
const SPREAD_RADIUS = 450

// let params = {
//   maxSpeed: 0.5
// }

// let warp = anime({
//   targets: params,
//   maxSpeed: 1.5,
//   easing: 'easeInOutQuad',
//   duration: 3000,
// })

// let notWarp = anime({
//   targets: params,
//   maxSpeed: 0.5,
//   easing: 'easeInOutQuad',
//   duration: 3000,
// })

const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
camera.position.z = 100

const renderer = new THREE.WebGLRenderer({
  canvas: document.querySelector('#canvas'),
  antialias: true,
  alpha: true
})
renderer.setSize(window.innerWidth, window.innerHeight)



const stats = new Stats()
document.querySelector('#app').appendChild(stats.dom)


let positions = []
let velocity = []
let acceleration = []
for(let i=0; i<PARTICLE_SIZE; i++) {
  let pos = new THREE.Vector3(
    THREE.MathUtils.randFloatSpread(SPREAD_RADIUS),
    THREE.MathUtils.randFloatSpread(SPREAD_RADIUS),
    THREE.MathUtils.randFloatSpread(SPREAD_RADIUS)
  )
  positions.push(
    pos,
    pos.clone()
  )
  
  velocity.push(0)
  acceleration.push(0.05)
}
const geo = new THREE.BufferGeometry().setFromPoints(positions)
// geo.setAttribute('position', new THREE.Float32BufferAttribute(positions, 6))
geo.setAttribute('velocity', new THREE.Float32BufferAttribute(velocity, 1))
geo.setAttribute('acceleration', new THREE.Float32BufferAttribute(acceleration, 1))

const mat = new THREE.LineBasicMaterial({
  color: 0xE6E6E6
})
const stars = new THREE.LineSegments(geo, mat)

const group = new THREE.Group()
group.add(stars)
scene.add(group)

let d = 0
function animate() {
  requestAnimationFrame(animate)
  
  let positions = stars.geometry.attributes.position.array
  let velocity = stars.geometry.attributes.velocity.array
  let acceleration = stars.geometry.attributes.acceleration.array
  let index = 0
  for(let i=0; i<PARTICLE_SIZE; i++) {
    let v = velocity[i]
    let a = acceleration[i]
    
    v += a
    
    v = THREE.MathUtils.clamp(v, 0, 3.5)
    
    let x = positions[index++]
    let y = positions[index++]
    let z = positions[index++]
    
    let xx = positions[index++]
    let yy = positions[index++]
    let zz = positions[index++]
    
    if(z > 100) {
      x = xx = THREE.MathUtils.randFloatSpread(SPREAD_RADIUS)
      y = yy = THREE.MathUtils.randFloatSpread(SPREAD_RADIUS)
      z = zz = -100
      // v = 1
      
      positions[index-3] = x
      positions[index-2] = y
      positions[index-6] = xx
      positions[index-5] = yy
    }
    
    z += v
    zz += v * 1.5
    
    velocity[i] = v
    positions[index-1] = zz
    positions[index-4] = z
  }
  stars.geometry.attributes.position.needsUpdate = true
  stars.geometry.attributes.velocity.needsUpdate = true
  
  stats.update()
  renderer.render(scene, camera)
}
animate()

window.addEventListener('resize', () => {
  camera.aspect = window.innerWidth / window.innerHeight
  camera.updateProjectionMatrix()
  renderer.setSize(window.innerWidth, window.innerHeight)
})

// document.querySelector('#app').addEventListener('mousedown', () => {
//   warp.restart()
// })

// document.querySelector('#app').addEventListener('mouseup', () => {
//   notWarp.restart()
// })
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.