Mischief

74 / Scenes

Scene Hero

A lit three-dimensional object behind a headline, steered by the pointer and coloured by the theme. The one component here that asks for three.

Built in the open

Steered by your pointer, coloured by your theme.

Installation

Copy the source into your project, or keep it behind a package.

npx shadcn@latest add Tinkerers-Labs/mischief-ui/scene-hero
import { SceneHero } from "mischief-ui/scene-hero"
Also installs
  • three

Or paste it in yourself. The source imports the shared cn helper from @/lib/utils, so point that at your own copy.

registry/default/scene-hero/scene-hero.tsx
"use client" import * as React from "react"import * as THREE from "three"import {  RenderSurface,  useThemeColors,  type SurfaceColor,} from "@/registry/default/render-surface/render-surface"import { cn } from "@/lib/utils" export type SceneHeroShape =  "torus-knot" | "icosahedron" | "capsule" | "box" | "torus" 

Usage

export function Hero() {
  return (
    <SceneHero shape="torus-knot" className="rounded-xl">
      <div className="px-8 py-20 text-center">
        <h1>Built in the open</h1>
      </div>
    </SceneHero>
  )
}

What it needs installed

This is the only component in the collection that reaches for three, and it is an optional peer like every other heavy dependency here. Nothing else in Mischief pulls it in, and the package root does not export this component, because a barrel holding it would fail to resolve for everyone who had not installed three.

npm install mischief-ui three
Import it from its own entry: mischief-ui/scene-hero, not the package root.

That is the trade. Around a hundred and fifty kilobytes for anyone who wants a lit object, and nothing at all for everyone else.

Why this one is allowed to be heavy

Everything else in this family is drawn with CSS, a two dimensional canvas, or a single shader, because those were enough. Real geometry, a metal surface that responds to two lights, and depth that survives being rotated are not things a gradient can imitate, and that is the bar a component has to clear before it may ask for a renderer.

The renderer is built once and survives a resize rather than being rebuilt, since a browser will only hand out a few GPU contexts and dragging a window edge should not spend them all.

Coloured by the page it is on

The material takes --primary and the fill light takes --foreground, both read from the mounted element and both updated on the next frame when the theme changes. The background stays transparent, so whatever the section behind it is painted with shows through and the object appears to be standing in the page rather than in a window cut into it.

<SceneHero shape="icosahedron" color="--ring" metalness={0.9} roughness={0.1} />

API

shape"torus-knot" | "icosahedron" | "capsule" | "box" | "torus"Which object to light. Defaults to "torus-knot".
colorstringColour of the object. Defaults to "--primary".
rimstringColour of the light that rims it. Defaults to "--foreground".
metalnessnumberZero to one. Defaults to 0.55.
roughnessnumberZero to one. Defaults to 0.25.
speednumberTurns per second at rest. Defaults to 0.12.
swaynumberHow far it leans toward the pointer, in radians. Defaults to 0.35.
pausedbooleanHolds the object still.

Accessibility

The object is decoration and the canvas is hidden from assistive technology, so a headline placed inside is read exactly as a headline. The lean follows the pointer and returns to centre when it leaves. Under reduced motion one frame is drawn and the object neither turns nor leans.