Skip to content
UI Kit
Accent color

Glowing orb animation

A soft, breathing orb of light, shaded like a sphere with a swirl inside and a wide glow, drawn with WebGL. Leans toward the pointer.

4 variantsOne of 6 components in 3D backgrounds

A calm light that seems to breathe

A glow orb is a soft sphere of light that breathes slowly, shaded like a real sphere and surrounded by a wide glow, drawn with WebGL. It gives a page one calm, living center, a good fit for assistants, voice features and quiet brands.

Hero with orb beside puts the copy on the left while the orb breathes on the right, on phones it rests below the copy. Rising orb lets the orb rise half above the bottom edge like a sun with the copy above. Featured card shows a small orb glowing in the card, like a voice that is listening. Behind a call to action glows a large orb softly behind the closing headline.

Let the orb stand alone, it needs space around it to glow. The card version works as a quiet sign that something is listening or thinking, for example next to an assistant feature.

Variants

Hero with orb besideNew in 1.2

Copy on the left, the orb breathes on the right. On phones it rests below the copy.

Rising orbNew in 1.2

The orb rises half above the bottom edge like a sun, the copy stands above.

Featured cardNew in 1.2

A small orb glows in the feature card, like a voice that is listening.

Behind a call to actionNew in 1.2

A large orb glows softly behind the closing headline.

Details

What Glow orb brings along, read from the registry and the source.

Variants
4
Version
1.0.0 · since 1.2.0
File
components/ui/glow-orb.tsx
Brings along
utilskitwebgl
npm packages
None
Languages
Texts come through props
Studio
Adjust in code
In collections
3D and motion

Props

The GlowOrbProps interface from components/ui/glow-orb.tsx, with the comments from the code. Also takes every prop of ShaderLayerProps.

  • placementoptional
    GlowOrbPlacement

    centerrightbottomcorner

    center behind the copy, right next to it (from 900 px box width, below the copy on narrower boxes), bottom half sunk below the edge, or corner at the top right (for cards)

  • sizeoptional
    number

    Size, 1 is the designed size

  • wobbleoptional
    number

    How much the outline moves, 0 is a calm circle, default 1

  • glowoptional
    number

    Width of the glow around the orb, default 1

Accessibility

Read from the source of the component.

  • Needs no client state. The server writes it in full, so it reads without JavaScript too.

Motion

  • No animations, it stands still from the start.

Source

The file aiw add puts into your project. From then on it is yours.

Show the file, 115 lines
components/ui/glow-orb.tsx
'use client';

import { cn } from '@/lib/kit';
import { GLSL_NOISE, ACCENT_PALETTE, ShaderBackdrop, ShaderCanvas, type ShaderLayerProps } from '@/components/ui/webgl';

/*
 * A soft orb of light that breathes. Its outline is a circle bent by slow
 * noise, its body is shaded like a sphere lit from the upper left, with a
 * swirl of the neighbour colors inside, a bright rim and a wide glow around
 * it. It leans toward the pointer and swells a little while the page scrolls
 * past. Soft by nature, rendered at reduced resolution.
 */

export type GlowOrbPlacement = 'center' | 'right' | 'bottom' | 'corner';

export interface GlowOrbProps extends ShaderLayerProps {
  /** center behind the copy, right next to it (from 900 px box width, below the copy on narrower boxes), bottom half sunk below the edge, or corner at the top right (for cards) */
  placement?: GlowOrbPlacement;
  /** Size, 1 is the designed size */
  size?: number;
  /** How much the outline moves, 0 is a calm circle, default 1 */
  wobble?: number;
  /** Width of the glow around the orb, default 1 */
  glow?: number;
}

const PLACE: Record<GlowOrbPlacement, number> = { center: 0, right: 1, bottom: 2, corner: 3 };

const FRAG = `${GLSL_NOISE}
uniform float u_place;uniform float u_size;uniform float u_wobble;uniform float u_glow;
void main(){
float m=min(u_res.x,u_res.y);
vec2 p=(gl_FragCoord.xy-.5*u_res)/m;
float hw=.5*u_res.x/m,hh=.5*u_res.y/m;
vec2 c=vec2(0.);float R=.27;
if(u_place>.5&&u_place<1.5){if(u_res.x/u_dpr>=900.){c=vec2(hw*.5,0.);R=.3;}else{R=min(.3,130.*u_dpr/m);c=vec2(0.,-hh+R*1.45+.04);}}
if(u_place>1.5&&u_place<2.5){R=min(.55,hw*.6);c=vec2(0.,-hh-R*.15);}
if(u_place>2.5){R=min(min(.3,hh*.42),80.*u_dpr/m);c=vec2(hw-R*1.05,hh-R*1.05);}
float t=u_time;
R*=u_size*(1.+.045*sin(t*.85)+(u_scroll-.5)*.08);
c+=(u_mouse-.5)*vec2(.12,.08)*u_hover;
vec2 d=p-c;float len=length(d);
vec2 dir=d/max(len,1e-4);
float w=(noise(dir*1.3+vec2(t*.22,-t*.17))-.5)*.22+(noise(dir*2.7-vec2(t*.31,t*.13))-.5)*.1;
float rr=R*(1.+w*u_wobble);
float sdf=len-rr;
vec2 q=d/rr;
float z=sqrt(max(0.,1.-dot(q,q)));
vec3 n=vec3(q,z);
float lit=clamp(dot(n,normalize(vec3(-.45,.55,.7))),0.,1.);
float sw=fbm(q*1.8+vec2(t*.09,-t*.07)+n.xy*.6);
vec3 hi=mix(vec3(1.),u_c3,u_dark);
vec3 body=mix(u_c0,hi,smoothstep(.25,1.,lit)*.62);
body=mix(body,u_c1,smoothstep(.45,.85,sw)*.32);
body=mix(body,u_c2,smoothstep(.55,.9,1.-sw)*.28*(1.-lit));
float rim=pow(1.-z,2.5);
body+=u_c0*rim*.3*u_dark;
float inside=smoothstep(.035,-.035,sdf);
float glow=exp(-max(sdf,0.)*mix(5.5,3.5,u_dark)/max(u_glow,.1))*mix(.32,.55,u_dark);
float ab=inside*mix(.74,.92,u_dark)*(.78+.22*z);
vec3 col=body*ab+u_c0*glow*(1.-inside);
float a=ab+glow*(1.-inside);
float k=u_intensity;
a=clamp(a*k,0.,1.);
gl_FragColor=vec4(min(col*k,vec3(1.)),a);
}`;

const SPOT: Record<GlowOrbPlacement, string> = {
  center: 'left-1/2 top-1/2 aspect-square w-[min(54%,54vh)] -translate-x-1/2 -translate-y-1/2',
  right: 'max-lg:left-1/2 max-lg:top-[64%] max-lg:aspect-square max-lg:w-[min(60%,40vh)] max-lg:-translate-x-1/2 lg:right-[10%] lg:top-1/2 lg:aspect-square lg:w-[min(32%,60vh)] lg:-translate-y-1/2',
  bottom: 'left-1/2 top-[78%] aspect-square w-[min(66%,40rem)] -translate-x-1/2',
  corner: 'right-[6%] top-[6%] aspect-square h-[44%]',
};

/**
 * A breathing orb of light behind or beside any content. With children it
 * renders a box, without children only the layer for a `relative` parent.
 */
export function GlowOrb({ intensity = 1, speed = 1, colors, interactive = true, placement = 'center', size = 1, wobble = 1, glow = 1, fallback, scrim = 'none', scrimStrength, scrimColor, children, className, contentClassName, layerClassName }: GlowOrbProps) {
  return (
    <ShaderBackdrop
      scrim={scrim}
      scrimStrength={scrimStrength}
      scrimColor={scrimColor}
      className={className}
      contentClassName={contentClassName}
      layer={
        <ShaderCanvas
          fragment={FRAG}
          colors={colors ?? ACCENT_PALETTE}
          uniforms={{ u_place: PLACE[placement], u_size: size, u_wobble: wobble, u_glow: glow }}
          speed={speed}
          intensity={intensity}
          scale={0.6}
          pointer={interactive}
          fallback={
            fallback ?? (
              <div
                className={cn('absolute rounded-full', SPOT[placement])}
                style={{
                  background: 'radial-gradient(circle at 38% 34%, color-mix(in oklab, var(--primary) 40%, var(--foreground) 25%), var(--primary) 45%, oklch(from var(--primary) calc(l * 0.8) c calc(h - 52)) 100%)',
                  boxShadow: '0 0 120px 30px color-mix(in oklab, var(--primary) 35%, transparent)',
                  opacity: 0.9,
                }}
              />
            )
          }
          className={layerClassName}
        />
      }
    >
      {children}
    </ShaderBackdrop>
  );
}

Frequent questions

How do I install Glow orb?

With aiw add @ai-website/glow-orb. The command puts components/ui/glow-orb.tsx into your project, the code is yours from then on. It brings along webgl. It needs no further npm packages.

Which variants does Glow orb have?

4 variants, Hero with orb beside, Rising orb, Featured card and Behind a call to action.

Can an AI assistant build with Glow orb?

Yes. Through the MCP server of the aiw CLI, Claude, Cursor, GitHub Copilot, Windsurf, Codex and Gemini find Glow orb with aiw_components and install it with aiw_add @ai-website/glow-orb.

Often searched

Jump to

Copied to the clipboard