Cycling Particle Formations With React Three Cannon

Here we render 144 particles and cycle the geometry of their formation, animating with the Cannon Physics library, in less lines of code than there are particles!

Demo / Source

const Particles = ({ formations }: Props) => {
  const radius = 0.1
  const [ref, api] = useSphere(index => ({
    mass: 1,
    args: radius,
    position: formations[0][index],
  }))

  const [index, setIndex] = useState(0)
  const cycleIndex = () =>
    void setIndex(p => (p < formations.length - 1 ? p + 1 : 0))

  useInterval(cycleIndex, 5000)

  useEffect(() => {
    let unsubscribers: any[] = []
    for (const [[x1, y1, z1], i] of formations[index].map(
      (v, i) => [v, i] as const
    )) {
      unsubscribers.push(
        api.at(i).position.subscribe(([x0, y0, z0]) => {
          api.at(i).velocity.set(x1 - x0, y1 - y0, z1 - z0)
        })
      )
    }
    return () =>
      void unsubscribers.reduce((_, unsubscribe) => void unsubscribe())
  }, [api, formations, index])

  return (
    <instancedMesh ref={ref} args={[null, null, formations[0].length] as any}>
      <sphereBufferGeometry args={[radius]} />
      <meshBasicMaterial />
    </instancedMesh>
  )
}

const App = () => {
  const formations = useMemo(() => {
    const planeVertices = getVertices(new PlaneBufferGeometry(5, 5, 11, 11))
    const icosahedronVertices = getVertices(
      new IcosahedronBufferGeometry(5, 3),
      1.0000000000000001
    ).slice(1, 145)
    const heartVertices = getHeart()
    return [planeVertices, icosahedronVertices, heartVertices]
  }, [])
  return (
    <Physics gravity={[0, 0, 0]}>
      <Particles formations={formations} />
    </Physics>
  )
}

Bonus: Make them Dance

With this simple change, the particles will dance around in a slightly chaotic yet beautiful fashion.

// useInterval(cycleIndex, 5000)
useInterval(cycleIndex, 500)

// ...

// api.at(i).velocity.set(x1 - x0, y1 - y0, z1 - z0)
api.at(i).applyImpulse([x1 - x0, y1 - y0, z1 - z0], [x0, y0, z0])

Here's another demo, check it out.

Get in touch

If you have any questions or ideas, please email me at tom@bearjam.dev.