# Best Served Bold Christmas Baubles

Physics, effects, lights and colors.

Demo: https://pmndrs.github.io/examples/examples/bestservedbold-christmas-baubles
Source: https://github.com/pmndrs/examples/tree/main/examples/bestservedbold-christmas-baubles
Scaffold: npx degit pmndrs/examples/examples/bestservedbold-christmas-baubles
Published: 2020-08-03
Authors: Paul Henschel
Tags: physics
Ported from: https://codesandbox.io/s/zxpv7
Dependencies: @react-three/drei@10.7.8, @react-three/fiber@9.6.1, @react-three/postprocessing@^3.0.4, @react-three/rapier@^2.2.0, react@19.2.8, react-dom@19.2.8, three@0.165.0
Binary files, in the repository but not below: src/adamsbridge.hdr, src/cap.glb

## src/App.tsx

```tsx
import * as THREE from "three";
import { useRef } from "react";
import { type GLTF } from "three-stdlib";
import { Canvas, useFrame } from "@react-three/fiber";
import { Environment, useGLTF } from "@react-three/drei";
import { EffectComposer, N8AO } from "@react-three/postprocessing";
import {
  BallCollider,
  Physics,
  RigidBody,
  CylinderCollider,
  type RapierRigidBody,
  type RigidBodyProps,
} from "@react-three/rapier";

import capModel from "./cap.glb?url";
import adamsbridgeHdr from "./adamsbridge.hdr?url";

type GLTFResult = GLTF & {
  nodes: { Mesh_1: THREE.Mesh };
};

// `legacyMode` was removed from @types/three; kept to match upstream source
(THREE.ColorManagement as unknown as { legacyMode: boolean }).legacyMode =
  false;
const baubleMaterial = new THREE.MeshLambertMaterial({
  color: "#c0a0a0",
  emissive: "red",
});
const capMaterial = new THREE.MeshStandardMaterial({
  metalness: 0.75,
  roughness: 0.15,
  color: "#8a492f",
  emissive: "#600000",
  envMapIntensity: 20,
});
const sphereGeometry = new THREE.SphereGeometry(1, 28, 28);
const baubles = [...Array(50)].map(() => ({
  scale: [0.75, 0.75, 1, 1, 1.25][Math.floor(Math.random() * 5)],
}));

function Bauble({
  vec = new THREE.Vector3(),
  scale,
  r = THREE.MathUtils.randFloatSpread,
}: {
  vec?: THREE.Vector3;
  scale: number;
  r?: (range: number) => number;
}) {
  const { nodes } = useGLTF(capModel) as unknown as GLTFResult;
  const api = useRef<RapierRigidBody>(null!);
  useFrame((state, delta) => {
    delta = Math.min(0.1, delta);
    api.current.applyImpulse(
      vec
        .copy(api.current.translation())
        .normalize()
        .multiply({
          x: -50 * delta * scale,
          y: -150 * delta * scale,
          z: -50 * delta * scale,
        }),
      false,
    );
  });
  const rigidBodyProps = {
    linearDamping: 0.75,
    angularDamping: 0.15,
    friction: 0.2,
    position: [r(20), r(20) - 25, r(20) - 10],
    ref: api,
    colliders: false,
    // `dispose` is no longer part of RigidBodyProps in this version; kept as-is
    // (unknown, then narrowed) to match the upstream source verbatim.
    dispose: null,
  } as unknown as RigidBodyProps;
  return (
    <RigidBody {...rigidBodyProps}>
      <BallCollider args={[scale]} />
      <CylinderCollider
        rotation={[Math.PI / 2, 0, 0]}
        position={[0, 0, 1.2 * scale]}
        args={[0.15 * scale, 0.275 * scale]}
      />
      <mesh
        castShadow
        receiveShadow
        scale={scale}
        geometry={sphereGeometry}
        material={baubleMaterial}
      />
      <mesh
        castShadow
        scale={2.5 * scale}
        position={[0, 0, -1.8 * scale]}
        geometry={nodes.Mesh_1.geometry}
        material={capMaterial}
      />
    </RigidBody>
  );
}

function Pointer({ vec = new THREE.Vector3() }: { vec?: THREE.Vector3 }) {
  const ref = useRef<RapierRigidBody>(null!);
  useFrame(({ mouse, viewport }) => {
    vec.lerp(
      {
        x: (mouse.x * viewport.width) / 2,
        y: (mouse.y * viewport.height) / 2,
        z: 0,
      },
      0.2,
    );
    ref.current?.setNextKinematicTranslation(vec);
  });
  return (
    <RigidBody
      position={[100, 100, 100]}
      type="kinematicPosition"
      colliders={false}
      ref={ref}
    >
      <BallCollider args={[2]} />
    </RigidBody>
  );
}

export const App = () => (
  <Canvas
    shadows
    gl={{ alpha: true, stencil: false, depth: false, antialias: false }}
    camera={{ position: [0, 0, 20], fov: 32.5, near: 1, far: 100 }}
    onCreated={(state) => (state.gl.toneMappingExposure = 1.5)}
  >
    <ambientLight intensity={Math.PI} />
    <spotLight
      position={[20, 20, 25]}
      intensity={Math.PI}
      decay={0}
      penumbra={1}
      angle={0.2}
      color="white"
      castShadow
      shadow-mapSize={[512, 512]}
    />
    <directionalLight position={[0, 5, -4]} intensity={4 * Math.PI} />
    <directionalLight
      position={[0, -15, -0]}
      intensity={4 * Math.PI}
      color="red"
    />
    <Physics gravity={[0, 0, 0]}>
      <Pointer />
      {
        baubles.map((props, i) => <Bauble key={i} {...props} />) /* prettier-ignore */
      }
    </Physics>
    <Environment files={adamsbridgeHdr} />
    {/* `disableNormalPass` no longer exists in this postprocessing version; the normal pass is already disabled by default */}
    <EffectComposer>
      <N8AO color="red" aoRadius={2} intensity={1} />
    </EffectComposer>
  </Canvas>
);
```

## src/DirtyFigmaExport.tsx

```tsx
import { type CSSProperties } from "react";

export function Underlay() {
  return (
    <div
      style={{
        position: "absolute",
        top: 0,
        left: 0,
        width: "100%",
        height: "100%",
        padding: 40,
        display: "inline-flex",
        flexDirection: "column",
        alignItems: "flex-start",
        justifyContent: "flex-start",
        pointerEvents: "none",
      }}
    >
      <div
        style={{
          width: "100%",
          padding: 0,
          display: "inline-flex",
          flexDirection: "row",
          alignItems: "center",
          justifyContent: "center",
        }}
      >
        <p
          style={{
            fontFamily: "'Antonio', sans-serif",
            flex: "1 1 0%",
            height: 30,
            fontSize: 30,
            fontWeight: "700",
            lineHeight: "30px",
            color: "black",
            letterSpacing: -2,
          }}
        >
          POIMANDRES
        </p>
        <div style={{ flex: "1 1 0%", display: "flex", gap: "2em" }}></div>
        <p
          style={{
            flex: "1 1 0%",
            height: 30,
            fontSize: 30,
            lineHeight: "30px",
            textAlign: "right",
            color: "black",
          }}
        >
          ⎑
        </p>
      </div>
      <div style={{ height: 60 }} />
      <div
        style={{
          width: "100%",
          padding: 0,
          display: "inline-flex",
          flexDirection: "row",
          alignItems: "flex-start",
          justifyContent: "center",
        }}
      >
        <p
          style={{
            flex: "1 1 0%",
            height: "100%",
            fontSize: 12,
            lineHeight: "1.5em",
            color: "black",
          }}
        >
          <b>Stones, Metals and Gems</b>
          <br />
          A Universal Deity
          <br />
          <b>—</b>
        </p>
        <div style={{ width: 10 }} />
        <p
          style={{
            transform: "rotate3d(0, 0, 1, 90deg) translate3d(100%,10px,0)",
            transformOrigin: "right",
            fontSize: 12,
            fontWeight: "700",
            lineHeight: "100%",
            textAlign: "right",
            color: "black",
            whiteSpace: "nowrap",
          }}
        >
          DRAG POINTER &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ●
        </p>
      </div>
      <div style={{ height: 10 }} />
      <div
        className="full"
        style={{
          fontFamily: "'Antonio', sans-serif",
          width: "100%",
          flex: "1 1 0%",
          padding: 0,
          display: "inline-flex",
          flexDirection: "row",
          alignItems: "flex-end",
          justifyContent: "center",
        }}
      >
        <p
          style={{
            flex: "1 1 0%",
            fontSize: 250,
            lineHeight: "1em",
            color: "black",
            margin: 0,
            letterSpacing: -10,
          }}
        >
          X
        </p>
        <div style={{ width: 10 }} />
        <p
          style={{
            flex: "1 1 0%",
            fontSize: 250,
            lineHeight: "100%",
            textAlign: "right",
            color: "black",
            margin: 0,
            letterSpacing: -10,
          }}
        >
          _01
        </p>
      </div>
      <div style={{ height: 60 }} />
      <div
        style={
          // `pointer` isn't a real CSS property; kept as-is (unknown, then
          // narrowed) to match the upstream source verbatim.
          {
            pointerEvents: "all",
            pointer: "auto",
            width: "100%",
            padding: 0,
            display: "inline-flex",
            flexDirection: "row",
            alignItems: "flex-end",
            justifyContent: "center",
          } as unknown as CSSProperties
        }
      >
        <p
          className="full"
          style={{
            whiteSpace: "nowrap",
            flex: "1 1 0%",
            fontSize: 12,
            lineHeight: "1.5em",
            color: "black",
          }}
        >
          <b>Wonders of Antiquity</b>
          <br />
          Pythagorean Mathematics
        </p>
        <div style={{ width: 10 }} />
        <p
          className="full"
          style={{
            fontFamily: "'Antonio', sans-serif",
            flex: "1 1 0%",
            fontSize: 16,
            fontWeight: "700",
            lineHeight: "1em",
            textAlign: "center",
            color: "black",
            letterSpacing: -0.5,
            whiteSpace: "nowrap",
          }}
        >
          THE SUMMIT OF THE MANY
        </p>
        <div style={{ width: 10 }} />
        <p
          className="full"
          style={{
            flex: "1 1 0%",
            fontSize: 12,
            lineHeight: "1em",
            textAlign: "right",
            color: "black",
          }}
        ></p>
      </div>
    </div>
  );
}

export function Overlay() {
  return (
    <div style={{ position: "absolute", bottom: 40, right: 40 }}>
      <p
        style={{
          flex: "1 1 0%",
          fontSize: 12,
          lineHeight: "1em",
          textAlign: "right",
          color: "black",
        }}
      >
        <a href="http://pmnd.rs/">pmnd.rs</a>{" "}
        <a href="https://github.com/pmndrs">git</a>{" "}
        <a href="https://codesandbox.io/s/zxpv7">csb</a>
      </p>
    </div>
  );
}
```

## src/index.tsx

```tsx
// Original: https://dribbble.com/shots/5708399-Christmas-Collisions
// By: 𝔅𝔢𝔰𝔱𝔖𝔢𝔯𝔳𝔢𝔡𝔅𝔬𝔩𝔡 @bstsrvdbld

import { createRoot } from "react-dom/client";
import { Suspense } from "react";
import { App } from "./App";
import { Underlay, Overlay } from "./DirtyFigmaExport";
import "./styles.css";

createRoot(document.getElementById("root")!).render(
  <>
    <Underlay />
    <Suspense fallback={null}>
      <App />
    </Suspense>
    <Overlay />
  </>,
);
```

## src/styles.css

```css
@import url("https://rsms.me/inter/inter.css");
@import url("https://fonts.googleapis.com/css2?family=Antonio:wght@600&display=swap");

* {
  box-sizing: border-box;
}

html,
body,
#root {
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  overflow: hidden;
  background: linear-gradient(180deg, #e6eaf5 0%, #f6f6f6 80%);
}

body {
  position: fixed;
  overflow: hidden;
  overscroll-behavior-y: none;
  font-family: "Inter var", sans-serif;
  cursor:
    url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMiAzMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTMwLjUgMTZDMzAuNSAyNC4wMDgxIDI0LjAwODEgMzAuNSAxNiAzMC41QzcuOTkxODcgMzAuNSAxLjUgMjQuMDA4MSAxLjUgMTZDMS41IDcuOTkxODcgNy45OTE4NyAxLjUgMTYgMS41QzI0LjAwODEgMS41IDMwLjUgNy45OTE4NyAzMC41IDE2WiIgc3Ryb2tlPSJibGFjayIvPgo8cGF0aCBkPSJNNiA5QzcuNjU2ODUgOSA5IDcuNjU2ODUgOSA2QzkgNC4zNDMxNSA3LjY1Njg1IDMgNiAzQzQuMzQzMTUgMyAzIDQuMzQzMTUgMyA2QzMgNy42NTY4NSA0LjM0MzE1IDkgNiA5WiIgZmlsbD0iYmxhY2siLz4KPC9zdmc+Cg=="),
    auto;
}

p {
  margin: 0;
  padding: 0;
}

a {
  padding-right: 10px;
  cursor: pointer;
  pointer-events: all;
  color: black;
  text-decoration: none; /* no underline */
}

@media screen and (max-width: 568px) {
  .full {
    visibility: hidden;
    display: none;
  }
}
```

## src/vite-env.d.ts

```ts
/// <reference types="vite/client" />
```
