Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions apps/web/src/lib/config/navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,10 @@ export const docsNavigation: DocItem[] = [
slug: "neural-noise",
name: "Neural Noise",
},
{
slug: "orb",
name: "Orb",
},
{
slug: "pixelated-image",
name: "Pixelated Image",
Expand Down
10 changes: 10 additions & 0 deletions apps/web/src/lib/docs/generated-manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,16 @@ export const docsManifest: ComponentInfo[] = [
three: "^0.182.0",
},
},
{
slug: "orb",
name: "Orb",
category: "canvas",
introducedAt: "2026-04-08",
dependencies: {
"@threlte/core": "^8.3.1",
three: "^0.182.0",
},
},
{
slug: "pixelated-image",
name: "Pixelated Image",
Expand Down
99 changes: 99 additions & 0 deletions apps/web/src/routes/docs/orb/+page.svx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
---
name: Orb
description: A 3D animated orb with state-driven flow patterns — idle drift, attune swirl, pulse breathing, surge churn — and smooth palette transitions between states.
---

<script lang="ts">
import ComponentPreview from "$lib/components/docs/ComponentPreview.svelte";
import ApiTable from "$lib/components/docs/ApiTable.svelte";
import InstallationTabs from "$lib/components/docs/LegacyInstallationTabs.svelte";
import Steps from "$lib/components/docs/markdown/Steps.svelte";
import Step from "$lib/components/docs/markdown/Step.svelte";

import orbSource from "motion-core/components/orb/Orb.svelte?raw";
import orbSceneSource from "motion-core/components/orb/OrbScene.svelte?raw";
import OrbDemo from "./OrbDemo.svelte";
import orbDemoSource from "./OrbDemo.svelte?raw";
</script>

## Installation

<Steps>
<Step title="Install the component">
Run the following command to install the component and its dependencies:
<InstallationTabs component="orb" />
</Step>
<Step title="Import the component">
Import the component into your Svelte file:

```ts
import { Orb } from "$lib/motion-core";
```

</Step>
</Steps>

## Usage

<ComponentPreview
sources={[
{ name: "Example", code: orbDemoSource, language: "svelte" },
{ name: "Orb.svelte", code: orbSource, language: "svelte" },
{ name: "OrbScene.svelte", code: orbSceneSource, language: "svelte" },
]}
>
<OrbDemo />
</ComponentPreview>

## Props

### Orb

<ApiTable
headers={["Prop", "Type", "Default"]}
keys={["prop", "type", "default"]}
data={[
{
prop: "state",
type: '"idle" | "attune" | "pulse" | "surge"',
default: '"idle"',
description:
"Controls the active animation mode. Each state drives a distinct flow pattern and motion dynamic.",
},
{
prop: "amplitude",
type: "number",
default: "0",
description:
"Audio amplitude in [0, 1]. Adds a reactive pulse layer on top of the current state animation.",
},
{
prop: "color",
type: "string",
default: '"#6933ff"',
description:
"Base color for the orb palette and rim glow. The full 5-color palette is derived from this hue.",
},
{
prop: "speed",
type: "number",
default: "1",
description: "Speed multiplier for the entire animation. 0.5 is half speed, 2 is double.",
},
{
prop: "class",
type: "string",
default: '""',
description: "Additional CSS classes for the container.",
},
]}
/>

### OrbState

| Value | Description |
|-------|-------------|
| `idle` | Gentle meandering drift with a deep purple palette. |
| `attune` | Tangential swirl — colors visibly rotate around the sphere. |
| `pulse` | Radial breathing — colors expand outward and contract back. |
| `surge` | Lissajous orbital churn — rapid multi-axis movement. |
54 changes: 54 additions & 0 deletions apps/web/src/routes/docs/orb/OrbDemo.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<script lang="ts">
import { Orb, type OrbState } from "motion-core";
import { cn } from "$lib/utils/cn";

const states: OrbState[] = ["surge", "attune", "pulse", "idle"];

let activeState = $state<OrbState>("surge");
let color = $state<string>("#6933ff");
let speed = $state<number>(1);
</script>

<Orb state={activeState} {color} {speed} class="h-full min-h-[36rem] w-full" />

<div
class="absolute bottom-14 left-1/2 z-10 flex w-fit -translate-x-1/2 flex-col items-center gap-3 rounded-sm bg-background-inset px-3 py-2.5 inset-shadow"
>
<div class="flex gap-1">
{#each states as s (s)}
<button
class={cn(
"gap-1.5 rounded-xs px-3 py-1 text-sm font-medium tracking-wide whitespace-nowrap uppercase transition-all duration-150 ease-out",
activeState === s
? "light:text-card bg-background-muted shadow-md card dark:text-foreground"
: "text-foreground-muted hover:text-foreground",
)}
onclick={() => (activeState = s)}
>
{s}
</button>
{/each}
</div>
<div class="flex items-center gap-4">
<label class="flex items-center gap-2 text-sm text-foreground-muted">
Color
<input
type="color"
bind:value={color}
class="h-6 w-14 cursor-pointer rounded-xs border-0 bg-transparent p-0"
/>
</label>
<label class="flex items-center gap-2 text-sm text-foreground-muted">
Speed
<input
type="range"
min="0.1"
max="3"
step="0.1"
bind:value={speed}
class="w-28 accent-foreground"
/>
<span class="w-7 text-right tabular-nums">{speed.toFixed(1)}x</span>
</label>
</div>
</div>
5 changes: 5 additions & 0 deletions apps/web/static/registry/components.json

Large diffs are not rendered by default.

37 changes: 37 additions & 0 deletions apps/web/static/registry/registry.json
Original file line number Diff line number Diff line change
Expand Up @@ -751,6 +751,43 @@
}
]
},
"orb": {
"slug": "orb",
"name": "Orb",
"description": "A 3D animated orb with state-driven flow patterns — idle drift, attune swirl, pulse breathing, surge churn — and smooth palette transitions between states.",
"category": "canvas",
"dependencies": {
"@threlte/core": "^8.3.1",
"three": "^0.182.0"
},
"devDependencies": {
"@types/three": "^0.182.0"
},
"internalDependencies": [],
"files": [
{
"path": "components/orb/Orb.svelte",
"kind": "entry"
},
{
"path": "components/orb/OrbScene.svelte"
},
{
"path": "components/orb/orb.glsl.ts"
},
{
"path": "components/orb/noise.glsl.ts"
},
{
"path": "components/orb/types.ts",
"typeExports": ["OrbState"]
},
{
"path": "utils/cn.ts",
"target": "utils"
}
]
},
"pixelated-image": {
"slug": "pixelated-image",
"name": "Pixelated Image",
Expand Down
2 changes: 1 addition & 1 deletion bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions packages/motion-core/src/lib/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,5 @@ export { default as VideoPlayer } from "./video-player/VideoPlayer.svelte";
export { default as GodRays } from "./god-rays/GodRays.svelte";
export { default as SpecularBand } from "./specular-band/SpecularBand.svelte";
export { default as Halo } from "./halo/Halo.svelte";
export { default as Orb } from "./orb/Orb.svelte";
export type { OrbState } from "./orb/types";
60 changes: 60 additions & 0 deletions packages/motion-core/src/lib/components/orb/Orb.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<script lang="ts">
import { Canvas } from "@threlte/core";
import { NoToneMapping } from "three";
import type { ComponentProps } from "svelte";
import Scene from "./OrbScene.svelte";
import { cn } from "../../utils/cn";
import type { OrbState } from "./types";

type SceneProps = ComponentProps<typeof Scene>;

interface Props {
/**
* Additional CSS classes for the container.
*/
class?: string;
/**
* Current animation state of the orb.
* - `idle`: gentle drift, purple palette.
* - `attune`: tangential swirl — colors rotate around the sphere.
* - `pulse`: radial breathing — colors expand and contract.
* - `surge`: lissajous orbital churn — rapid multi-axis movement.
* @default "idle"
*/
state?: OrbState;
/**
* Audio amplitude in [0, 1] that adds a reactive pulse layer.
* @default 0
*/
amplitude?: SceneProps["amplitude"];
/**
* Base color for the orb palette and rim glow. Accepts any CSS color string.
* @default "#6933ff"
*/
color?: SceneProps["color"];
/**
* Speed multiplier applied to the entire animation.
* @default 1
*/
speed?: SceneProps["speed"];

[key: string]: unknown;
}

let {
class: className = "",
state = "idle",
amplitude = 0,
color = "#6933ff",
speed = 1,
...rest
}: Props = $props();
</script>

<div class={cn("relative h-full w-full overflow-hidden", className)} {...rest}>
<div class="absolute inset-0">
<Canvas toneMapping={NoToneMapping} rendererParameters={{ alpha: true }}>
<Scene orbState={state} {amplitude} {color} {speed} />
</Canvas>
</div>
</div>
Loading