diff --git a/apps/people/src/app/components/StarBackground.tsx b/apps/people/src/app/components/StarBackground.tsx
new file mode 100644
index 0000000..cdcfcee
--- /dev/null
+++ b/apps/people/src/app/components/StarBackground.tsx
@@ -0,0 +1,164 @@
+'use client'
+
+import React, { useEffect } from 'react'
+
+interface Props {
+ speedFactor?: number
+ backgroundColor?: string
+ starColor?: [number, number, number]
+ starCount?: number
+}
+
+export default function StarBackground(props: Props) {
+ const {
+ speedFactor = 0.05,
+ backgroundColor = 'black',
+ starColor = [255, 255, 255],
+ starCount = 5000,
+ } = props
+
+ useEffect(() => {
+ const canvas = document.getElementById(
+ 'starfield'
+ ) as HTMLCanvasElement | null
+
+ if (canvas) {
+ const c = canvas.getContext('2d')
+
+ if (c) {
+ let w = window.innerWidth
+ let h = window.innerHeight
+
+ const setCanvasExtents = () => {
+ canvas.width = w
+ canvas.height = h
+ }
+
+ setCanvasExtents()
+
+ window.onresize = () => {
+ setCanvasExtents()
+ }
+
+ const makeStars = (count: number) => {
+ const out = []
+ for (let i = 0; i < count; i++) {
+ const s = {
+ x: Math.random() * 1600 - 800,
+ y: Math.random() * 900 - 450,
+ z: Math.random() * 1000,
+ }
+ out.push(s)
+ }
+ return out
+ }
+
+ const stars = makeStars(starCount)
+
+ const clear = () => {
+ c.fillStyle = backgroundColor
+ c.fillRect(0, 0, canvas.width, canvas.height)
+ }
+
+ const putPixel = (x: number, y: number, brightness: number) => {
+ const rgb =
+ 'rgba(' +
+ starColor[0] +
+ ',' +
+ starColor[1] +
+ ',' +
+ starColor[2] +
+ ',' +
+ brightness +
+ ')'
+ c.fillStyle = rgb
+ c.fillRect(x, y, 1, 1)
+ }
+
+ const moveStars = (distance: number) => {
+ const count = stars.length
+ for (let i = 0; i < count; i++) {
+ const s = stars[i]
+ s.z -= distance
+ while (s.z <= 1) {
+ s.z += 1000
+ }
+ }
+ }
+
+ let prevTime: number
+ const init = (time: number) => {
+ prevTime = time
+ requestAnimationFrame(tick)
+ }
+
+ const tick = (time: number) => {
+ const elapsed = time - prevTime
+ prevTime = time
+
+ moveStars(elapsed * speedFactor)
+
+ clear()
+
+ const cx = w / 2
+ const cy = h / 2
+
+ const count = stars.length
+ for (let i = 0; i < count; i++) {
+ const star = stars[i]
+
+ const x = cx + star.x / (star.z * 0.001)
+ const y = cy + star.y / (star.z * 0.001)
+
+ if (x < 0 || x >= w || y < 0 || y >= h) {
+ continue
+ }
+
+ const d = star.z / 1000.0
+ const b = 1 - d * d
+
+ putPixel(x, y, b)
+ }
+
+ requestAnimationFrame(tick)
+ }
+
+ requestAnimationFrame(init)
+
+ // add window resize listener:
+ window.addEventListener('resize', function () {
+ w = window.innerWidth
+ h = window.innerHeight
+ setCanvasExtents()
+ })
+ } else {
+ console.error('Could not get 2d context from canvas element')
+ }
+ } else {
+ console.error('Could not find canvas element with id "starfield"')
+ }
+
+ return () => {
+ window.onresize = null
+ }
+ }, [starColor, backgroundColor, speedFactor, starCount])
+
+ return (
+
+ )
+}
diff --git a/apps/people/src/app/page.tsx b/apps/people/src/app/page.tsx
index 032bb6d..1953706 100644
--- a/apps/people/src/app/page.tsx
+++ b/apps/people/src/app/page.tsx
@@ -6,6 +6,7 @@ import { SuspenseImage } from '@suspensive/react-image'
import { Reorder, motion } from 'framer-motion'
import { useState } from 'react'
import { Card, type CardData } from './components/Card'
+import StarBackground from './components/StarBackground'
import { initialCards } from '~/mock/cardDatas'
export default function Home() {
@@ -15,13 +16,13 @@ export default function Home() {
>(null)
return (
-
+
+