diff --git a/src/app/badge/page.jsx b/src/app/badge/page.jsx
new file mode 100644
index 0000000..86694c9
--- /dev/null
+++ b/src/app/badge/page.jsx
@@ -0,0 +1,335 @@
+"use client";
+
+import * as THREE from "three";
+import { useEffect, useRef, useState } from "react";
+import { Canvas, extend, useThree, useFrame } from "@react-three/fiber";
+import {
+ useGLTF,
+ useTexture,
+ Environment,
+ Lightformer,
+ RenderTexture,
+ Text3D,
+ Resize,
+ Center,
+ PerspectiveCamera,
+} from "@react-three/drei";
+import {
+ BallCollider,
+ CuboidCollider,
+ Physics,
+ RigidBody,
+ useRopeJoint,
+ useSphericalJoint,
+} from "@react-three/rapier";
+import { MeshLineGeometry, MeshLineMaterial } from "meshline";
+import { useControls } from "leva";
+
+extend({ MeshLineGeometry, MeshLineMaterial });
+
+useTexture.preload(
+ "https://github.com/user-attachments/assets/999b5d58-ac8a-4c20-8fc6-74e8ab7876e7",
+);
+
+export default function App() {
+ const { debug } = useControls({ debug: false });
+ return (
+
+ );
+}
+
+function BadgeTexture() {
+ // const viewport = useThree((state) => state.viewport);
+
+ return (
+ <>
+
+ {/*
+
+
+ */}
+
+
+
+ hyamero
+
+
+ omsimos.com
+
+
+
+ >
+ );
+}
+
+function Band({ maxSpeed = 50, minSpeed = 10 }) {
+ const band = useRef(), fixed = useRef(), j1 = useRef(), j2 = useRef(), j3 = useRef(), card = useRef() // prettier-ignore
+ const vec = new THREE.Vector3(), ang = new THREE.Vector3(), rot = new THREE.Vector3(), dir = new THREE.Vector3() // prettier-ignore
+ const segmentProps = {
+ type: "dynamic",
+ canSleep: true,
+ colliders: false,
+ angularDamping: 2,
+ linearDamping: 2,
+ };
+ useGLTF.preload(
+ "https://assets.vercel.com/image/upload/contentful/image/e5382hct74si/5huRVDzcoDwnbgrKUo1Lzs/53b6dd7d6b4ffcdbd338fa60265949e1/tag.glb",
+ );
+
+ const { nodes, materials } = useGLTF(
+ "https://assets.vercel.com/image/upload/contentful/image/e5382hct74si/5huRVDzcoDwnbgrKUo1Lzs/53b6dd7d6b4ffcdbd338fa60265949e1/tag.glb",
+ );
+
+ // const texture = useTexture(
+ // "https://assets.vercel.com/image/upload/contentful/image/e5382hct74si/SOT1hmCesOHxEYxL7vkoZ/c57b29c85912047c414311723320c16b/band.jpg",
+ // );
+
+ const texture = useTexture("/band.jpg");
+ const { width, height } = useThree((state) => state.size);
+ const [curve] = useState(
+ () =>
+ new THREE.CatmullRomCurve3([
+ new THREE.Vector3(),
+ new THREE.Vector3(),
+ new THREE.Vector3(),
+ new THREE.Vector3(),
+ ]),
+ );
+ const [dragged, drag] = useState(false);
+ const [hovered, hover] = useState(false);
+
+ useRopeJoint(fixed, j1, [[0, 0, 0], [0, 0, 0], 1]) // prettier-ignore
+ useRopeJoint(j1, j2, [[0, 0, 0], [0, 0, 0], 1]) // prettier-ignore
+ useRopeJoint(j2, j3, [[0, 0, 0], [0, 0, 0], 1]) // prettier-ignore
+ useSphericalJoint(j3, card, [[0, 0, 0], [0, 1.45, 0]]) // prettier-ignore
+
+ useEffect(() => {
+ if (hovered) {
+ document.body.style.cursor = dragged ? "grabbing" : "grab";
+ return () => void (document.body.style.cursor = "auto");
+ }
+ }, [hovered, dragged]);
+
+ useFrame((state, delta) => {
+ if (dragged) {
+ vec.set(state.pointer.x, state.pointer.y, 0.5).unproject(state.camera);
+ dir.copy(vec).sub(state.camera.position).normalize();
+ vec.add(dir.multiplyScalar(state.camera.position.length()));
+ [card, j1, j2, j3, fixed].forEach((ref) => ref.current?.wakeUp());
+ card.current?.setNextKinematicTranslation({
+ x: vec.x - dragged.x,
+ y: vec.y - dragged.y,
+ z: vec.z - dragged.z,
+ });
+ }
+ if (fixed.current) {
+ // Fix most of the jitter when over pulling the card
+ [j1, j2].forEach((ref) => {
+ if (!ref.current.lerped)
+ ref.current.lerped = new THREE.Vector3().copy(
+ ref.current.translation(),
+ );
+ const clampedDistance = Math.max(
+ 0.1,
+ Math.min(1, ref.current.lerped.distanceTo(ref.current.translation())),
+ );
+ ref.current.lerped.lerp(
+ ref.current.translation(),
+ delta * (minSpeed + clampedDistance * (maxSpeed - minSpeed)),
+ );
+ });
+ // Calculate catmul curve
+ curve.points[0].copy(j3.current.translation());
+ curve.points[1].copy(j2.current.lerped);
+ curve.points[2].copy(j1.current.lerped);
+ curve.points[3].copy(fixed.current.translation());
+ band.current.geometry.setPoints(curve.getPoints(32));
+ // Tilt it back towards the screen
+ ang.copy(card.current.angvel());
+ rot.copy(card.current.rotation());
+ card.current.setAngvel({ x: ang.x, y: ang.y - rot.y * 0.25, z: ang.z });
+ }
+ });
+
+ curve.curveType = "chordal";
+ texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+ hover(true)}
+ onPointerOut={() => hover(false)}
+ onPointerUp={(e) => (
+ e.target.releasePointerCapture(e.pointerId), drag(false)
+ )}
+ onPointerDown={(e) => (
+ e.target.setPointerCapture(e.pointerId),
+ drag(
+ new THREE.Vector3()
+ .copy(e.point)
+ .sub(vec.copy(card.current.translation())),
+ )
+ )}
+ >
+ {/*
+
+ */}
+
+
+
+ {/* */}
+
+
+
+
+
+ {/*
+
+
+ Joseph Dale
+
+
+ Omsimos
+
+
+ */}
+
+
+
+
+
+
+
+
+
+ >
+ );
+}
diff --git a/src/app/page.tsx b/src/app/page.tsx
index e843eac..7343d4b 100644
--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -16,12 +16,9 @@ export default async function Home() {
)}
>
-
- ✨ Contribute to GitHub
+
+ ✨ Experimental:
+ Visit 3D Badge