All shaders
Dithering Globe Arcs Hero
SectionA centered hero section with a rising ordered-dither globe and flight arcs between cities.
dithering-globe-arcs-hero.tsx
import { cn } from "@/lib/utils"
import { buttonVariants } from "@/components/ui/button"
import { DitheringGlobeArcs, type GlobeArc } from "./dithering-globe-arcs"
type DitheringGlobeArcsHeroProps = React.ComponentProps<"section"> & {
eyebrow?: string
title?: string
description?: string
primaryAction?: { label: string; href: string }
secondaryAction?: { label: string; href: string }
arcs?: GlobeArc[]
colorBack?: string
colorFront?: string
colorArc?: string
}
function DitheringGlobeArcsHero({
eyebrow = "Global edge network",
title = "Closer to every user, everywhere",
description = "Requests route to the nearest region in milliseconds. Watch them fly across a live, ordered-dither globe.",
primaryAction = { label: "Deploy now", href: "#" },
secondaryAction = { label: "See regions", href: "#" },
arcs,
colorBack = "#000000",
colorFront = "#38bdf8",
colorArc = "#ffffff",
className,
style,
...props
}: DitheringGlobeArcsHeroProps) {
return (
<section
data-slot="dithering-globe-arcs-hero"
className={cn(
"dark relative isolate flex w-full flex-col items-center overflow-hidden px-6 pt-24 text-center text-white",
className
)}
style={{ backgroundColor: colorBack, ...style }}
{...props}
>
<div className="flex max-w-2xl flex-col items-center gap-6">
<span
className="font-mono text-[11px] tracking-widest uppercase"
style={{ color: colorFront }}
>
{eyebrow}
</span>
<h1 className="font-heading text-4xl leading-[1.05] tracking-tight text-balance sm:text-6xl">
{title}
</h1>
<p className="max-w-md text-base text-pretty text-white/70 sm:text-lg">
{description}
</p>
<div className="flex flex-wrap items-center justify-center gap-3">
<a
href={primaryAction.href}
className={cn(
buttonVariants({ size: "lg" }),
"bg-white text-black hover:bg-white/80"
)}
>
{primaryAction.label}
</a>
<a
href={secondaryAction.href}
className={buttonVariants({ size: "lg", variant: "outline" })}
>
{secondaryAction.label}
</a>
</div>
</div>
{/* The globe is centered on the bottom edge, so only its top half shows. */}
<div className="pointer-events-none relative -z-10 h-[440px] w-full">
<div className="absolute bottom-0 left-1/2 aspect-square w-[min(1100px,160%)] -translate-x-1/2 translate-y-1/2">
<DitheringGlobeArcs
aria-hidden
className="size-full"
arcs={arcs}
colorBack={colorBack}
colorFront={colorFront}
colorArc={colorArc}
scale={0.7}
/>
</div>
</div>
</section>
)
}
export { DitheringGlobeArcsHero }
dithering-globe-arcs.tsx
"use client"
import * as React from "react"
import {
getShaderColorFromString,
ShaderMount,
type ShaderComponentProps,
} from "@paper-design/shaders-react"
import { GLOBE_LAND_TEXTURE } from "./globe-texture"
const MAX_ARCS = 12
const globeArcsFragmentShader = `#version 300 es
precision mediump float;
#define MAX_ARCS ${MAX_ARCS}
#define ARC_SEGMENTS 24
uniform float u_time;
uniform vec2 u_resolution;
uniform float u_pixelRatio;
uniform sampler2D u_land;
uniform vec4 u_colorBack;
uniform vec4 u_colorFront;
uniform vec4 u_colorArc;
uniform float u_pxSize;
uniform float u_scale;
uniform float u_tilt;
uniform float u_glow;
uniform vec3 u_arcFrom[MAX_ARCS];
uniform vec3 u_arcTo[MAX_ARCS];
uniform float u_arcCount;
out vec4 fragColor;
const float PI = 3.14159265359;
const int bayer4x4[16] = int[16](
0, 8, 2, 10,
12, 4, 14, 6,
3, 11, 1, 9,
15, 7, 13, 5
);
float getBayerValue(vec2 cell) {
ivec2 pos = ivec2(mod(cell, 4.));
return float(bayer4x4[pos.y * 4 + pos.x]) / 16.;
}
// Spins a point on the globe around its axis, then tilts it towards the
// viewer. The inverse of the lookup used to texture the globe below.
mat3 viewMatrix(float spin) {
float cs = cos(spin);
float ss = sin(spin);
float c = cos(u_tilt);
float s = sin(u_tilt);
mat3 spinMatrix = mat3(cs, 0., -ss, 0., 1., 0., ss, 0., cs);
mat3 tiltMatrix = mat3(1., 0., 0., 0., c, -s, 0., s, c);
return tiltMatrix * spinMatrix;
}
// A point is hidden when it sits behind the globe's disc.
float isVisible(vec3 v) {
return (v.z > 0. || dot(v.xy, v.xy) > 1.) ? 1. : 0.;
}
float segmentDistance(vec2 p, vec2 a, vec2 b, out float h) {
vec2 pa = p - a;
vec2 ba = b - a;
h = clamp(dot(pa, ba) / max(dot(ba, ba), 1e-6), 0., 1.);
return length(pa - ba * h);
}
void main() {
// Snap to the dithering grid so every "pixel" is a solid block.
float pxSize = u_pxSize * u_pixelRatio;
vec2 cell = floor((gl_FragCoord.xy - .5 * u_resolution) / pxSize);
vec2 cellCenter = (cell + .5) * pxSize;
// Unit circle that fits the canvas, scaled by u_scale.
float radius = .5 * min(u_resolution.x, u_resolution.y) * u_scale;
vec2 p = cellCenter / radius;
float r2 = dot(p, p);
float spin = .25 * u_time;
float shape = 0.;
if (r2 < 1.) {
// Texture lookups need more precision than mediump guarantees on mobile.
highp vec3 normal = vec3(p, sqrt(1. - r2));
// Tilt the globe towards the viewer, then spin it around its own axis.
float c = cos(u_tilt);
float s = sin(u_tilt);
highp vec3 q = vec3(normal.x, c * normal.y - s * normal.z, s * normal.y + c * normal.z);
highp float lon = atan(q.x, q.z) - spin;
highp float lat = asin(clamp(q.y, -1., 1.));
highp vec2 uv = vec2(fract(lon / (2. * PI) + .5), .5 - lat / PI);
float land = texture(u_land, uv).r;
land = max(land, step(.985, uv.y)); // close the gap at the south pole
vec3 light = normalize(vec3(-.5, .6, .75));
float diffuse = clamp(dot(normal, light), 0., 1.);
float rim = pow(1. - normal.z, 3.);
// Keep the globe a little dimmer so the arcs stand out on top of it.
shape = mix(.1, .85, land) * (.25 + .75 * diffuse) + .2 * rim;
} else {
// Soft atmosphere around the edge.
shape = u_glow * exp(-(sqrt(r2) - 1.) * 18.);
}
// Arcs and city pins, in globe units. Widths are in dithering pixels.
float unit = pxSize / radius;
float lineWidth = 1.2 * unit;
float pinRadius = 2.5 * unit;
float arc = 0.;
mat3 view = viewMatrix(spin);
for (int i = 0; i < MAX_ARCS; i++) {
if (float(i) >= u_arcCount) break;
vec3 from = u_arcFrom[i];
vec3 to = u_arcTo[i];
float omega = acos(clamp(dot(from, to), -1., 1.));
float sinOmega = max(sin(omega), 1e-4);
// Longer flights climb higher above the surface.
float height = .05 + .25 * omega / PI;
// Every point of the arc, its pins included, lies within this distance of
// the arc's midpoint. Skipping arcs that are out of reach keeps the cost
// per pixel low.
vec3 mid = from + to;
if (dot(mid, mid) > 1e-6) {
vec2 center = (view * normalize(mid)).xy;
float reach = 2. * sin(.25 * omega) + height + lineWidth + 4. * pinRadius + unit;
if (length(p - center) > reach) continue;
}
// Each arc draws itself in, then retracts, on its own schedule.
float phase = fract(.12 * u_time + float(i) * .371);
float head = smoothstep(0., .6, phase);
float tail = smoothstep(.45, 1., phase);
vec3 prev = view * from;
for (int j = 1; j <= ARC_SEGMENTS; j++) {
float t = float(j) / float(ARC_SEGMENTS);
vec3 point = (sin((1. - t) * omega) * from + sin(t * omega) * to) / sinOmega;
point *= 1. + height * sin(PI * t);
vec3 next = view * point;
float h;
float d = segmentDistance(p, prev.xy, next.xy, h);
float along = (float(j - 1) + h) / float(ARC_SEGMENTS);
float drawn = step(tail, along) * step(along, head);
// Brighter towards the leading end, like a comet.
float brightness = mix(.45, 1., clamp((along - tail) / max(head - tail, 1e-4), 0., 1.));
float visible = isVisible(mix(prev, next, h));
arc = max(arc, step(d, lineWidth) * drawn * brightness * visible);
prev = next;
}
// Pins at both ends, with a ring that pulses outwards.
vec3 ends[2] = vec3[2](view * from, view * to);
for (int k = 0; k < 2; k++) {
float d = length(p - ends[k].xy);
float pulse = fract(.5 * u_time + float(i) * .23 + float(k) * .5);
float ring = step(abs(d - pinRadius * (1. + 3. * pulse)), .6 * unit) * (1. - pulse);
float pin = max(step(d, pinRadius), ring * .8);
arc = max(arc, pin * step(0., ends[k].z));
}
}
// Same threshold as Paper's Dithering shader: zero intensity stays empty.
float bayer = getBayerValue(cell) - .5;
float res = step(.5, shape + bayer);
float arcRes = step(.5, arc + bayer);
vec3 fgColor = mix(u_colorFront.rgb * u_colorFront.a, u_colorArc.rgb * u_colorArc.a, arcRes);
float fgOpacity = mix(u_colorFront.a, u_colorArc.a, arcRes);
res = max(res, arcRes);
vec3 bgColor = u_colorBack.rgb * u_colorBack.a;
float bgOpacity = u_colorBack.a;
vec3 color = fgColor * res;
float opacity = fgOpacity * res;
color += bgColor * (1. - opacity);
opacity += bgOpacity * (1. - opacity);
fragColor = vec4(color, opacity);
}
`
/** A latitude / longitude pair, in degrees. */
type Coordinates = [lat: number, lon: number]
type GlobeArc = { from: Coordinates; to: Coordinates }
const CITIES = {
sanFrancisco: [37.77, -122.42],
newYork: [40.71, -74.01],
saoPaulo: [-23.55, -46.63],
london: [51.51, -0.13],
lagos: [6.52, 3.38],
dubai: [25.2, 55.27],
singapore: [1.35, 103.82],
tokyo: [35.68, 139.69],
sydney: [-33.87, 151.21],
} satisfies Record<string, Coordinates>
const DEFAULT_ARCS: GlobeArc[] = [
{ from: CITIES.sanFrancisco, to: CITIES.newYork },
{ from: CITIES.newYork, to: CITIES.london },
{ from: CITIES.london, to: CITIES.dubai },
{ from: CITIES.dubai, to: CITIES.singapore },
{ from: CITIES.singapore, to: CITIES.tokyo },
{ from: CITIES.tokyo, to: CITIES.sanFrancisco },
{ from: CITIES.newYork, to: CITIES.saoPaulo },
{ from: CITIES.london, to: CITIES.lagos },
{ from: CITIES.singapore, to: CITIES.sydney },
]
// Unit vector in the same space the shader samples the land texture in.
function toVector([lat, lon]: Coordinates): [number, number, number] {
const phi = (lat * Math.PI) / 180
const lambda = (lon * Math.PI) / 180
return [
Math.cos(phi) * Math.sin(lambda),
Math.sin(phi),
Math.cos(phi) * Math.cos(lambda),
]
}
type DitheringGlobeArcsProps = Omit<ShaderComponentProps, "ref"> & {
/** Up to 12 flight paths, drawn as arcs with pins at each end. */
arcs?: GlobeArc[]
colorBack?: string
colorFront?: string
colorArc?: string
/** Size of a dithering pixel, in CSS pixels. */
size?: number
/** Globe diameter relative to the smaller side of the container. */
scale?: number
/**
* Axial tilt, in degrees. Negative values tip the north pole towards the
* viewer, positive values the south pole.
*/
tilt?: number
/** Strength of the atmosphere glow around the globe, 0 to 1. */
glow?: number
speed?: number
}
function DitheringGlobeArcs({
arcs = DEFAULT_ARCS,
colorBack = "#00000000",
colorFront = "#38bdf8",
colorArc = "#ffffff",
size = 2,
scale = 0.9,
tilt = -20,
glow = 0.4,
speed = 1,
...props
}: DitheringGlobeArcsProps) {
const uniforms = React.useMemo(() => {
// Uniform arrays have a fixed length, so pad the unused slots.
const padded = Array.from(
{ length: MAX_ARCS },
(_, i) => arcs[i] ?? DEFAULT_ARCS[0]
)
return {
u_land: GLOBE_LAND_TEXTURE,
u_colorBack: getShaderColorFromString(colorBack),
u_colorFront: getShaderColorFromString(colorFront),
u_colorArc: getShaderColorFromString(colorArc),
u_pxSize: size,
u_scale: scale,
u_tilt: (tilt * Math.PI) / 180,
u_glow: glow,
u_arcFrom: padded.map((arc) => toVector(arc.from)),
u_arcTo: padded.map((arc) => toVector(arc.to)),
u_arcCount: Math.min(arcs.length, MAX_ARCS),
}
}, [arcs, colorBack, colorFront, colorArc, size, scale, tilt, glow])
return (
<ShaderMount
data-slot="dithering-globe-arcs"
fragmentShader={globeArcsFragmentShader}
uniforms={uniforms}
speed={speed}
{...props}
/>
)
}
export { DitheringGlobeArcs, type GlobeArc }
globe-texture.ts
// Equirectangular land mask (1024×512, 1-bit PNG) rendered from Natural Earth
// 1:50m land polygons, which are in the public domain.
// White = land, black = water. North is up, longitude -180° is on the left.
const GLOBE_LAND_TEXTURE =
"data:image/png;base64," +
[
"iVBORw0KGgoAAAANSUhEUgAABAAAAAIAAQAAAAA0tiMUAAAczElEQVR42u2dz28nyVXAX7kdt5d4",
"XV6CtCNl4vYiRDgk2okWKSMwbktIEQek/AesIw7kgLSzHIgJznRtLJhIRMxyywHFi5AQEhxy3EOU",
"aWsjOQfYiQQSURTYHkaKF4LWPRqR6Vm3u6Cq+ue36/e3PV8iXIdZr93fb3/61XuvXr1XVQ1w1a7a",
"VbtqV+2qXbWrdtWcW0hp9cxv+kLa/JRQ3sizvPvL/JYZ//k5Sp81QRDUt0wBUJk8cwAcN7eEpDqg",
"jzjBWQ757jO6P20fuSXhLYe4UH/s2mQajxqZVzAEeKixhWI6ASQ5pZR3fPolOttS6UcQRN9cD9q/",
"bSXZHPdHtMLx6U8opUU4ur/8OX/n82mYJX/Z/YKW81gApR9cY2pQpYlWACh7t+m0MoBy4LZy//uv",
"s8eOhQagmdtX233Quz2BEICeheCzKp+nCyhN+P1HAD1HgMrT92UacYP/G1NfncQ5wG8ltBTSDjUA",
"9LxUaz6m2b4fwBLlt/0b7obxSAV6FyZU19HRIy+3mZCA5lVAC+51otH9e3dE9/6R7miHTx8tWAEU",
"lEm681dc3GMb6Lviz0VllGpEUIG7336lYhaFU0AnTAXGEvhgua+rBS6nHQLWghKuAwRleCh08Euz",
"CIMb0nxdJ2XsbAf8edYaX1TyfzVKAImmA2AXEmcdSMobsMfUp0LC56F4dP/+l+KRmof5wJs4mkFY",
"vZ6hHB3Hx/SC0ouEKdLIEW9pv4L2hqB4aLUWLSpCSgo4P0uh+SD+8cxwXOkAot5Q/erDXkT3v2P1",
"/g0bERzRjBl/uhWnbQcjlSOSjWH9nwcq8wVKbcZn9rSPSPywSHoKPxTBN3SfJr0Yng4kENDz/EcW",
"IrhP6TskPizjHsBAD4ptzRBW9Eez4eAdUJomhY0aiHEuPuukFygGo3EPpLMd0HMbt2zCkFQ5OLft",
"HtEYEYx64DB1i4OEjFAaDx2eMSDrSW/AXLYj8hYy+wQkdLwEmtMyKKN72XBaNnbFM24s60LKRgWT",
"4xKQAKM2EqAZrFdAU5pHD+VmQHQjOY8Sex1QsXCCIPFFsc30lz4+xMV6VNAsPqFn1RhAowKIPyJ6",
"8xqcNFef839/YjurxiwKOT0tMC0PILlPs6S+22sdwHsaI+DPiauV9dEQVomBxmiC9BGl9LQAWuwD",
"PaXvNlbRk8C5xghKEYWsRHkiieOweWaLKZPd4xIiBnDWqXxs5YmF5UTl9nkVjSSwCyEtB2ZeyDqR",
"iKtDqICNh7QSV+1YKWEkBF1U4ziOQv708Uws0ehFP3S+BfVkIKSALzqrb2eqtIrVjmWFqUxAq+Sc",
"nswCvDErv7DLgpQjn0cgoLXYq3SQq6DJKAgN/+Vb3YyWA8SUPpkFOJ/1I2u9OzaWtdb8omBOOeo5",
"vm5ywsLVYbfdS+jMtDai9DFVtmo8/FJ6zCeEm+3AR0Kasd6IKH0yzFaMlPCjfd8YZIBoGVNdq3jo",
"P+vl2Xd85d76TgPwBZpCmH6usRz8WOWJuWy6OfmXMqYDWgA27TrpvqYbYs5eox8kGRY3ufh4ycz6",
"bj2eBw/obHzRHyWEVLe5AElS6SXArg9bA+9c1p3ayDCtcBVWuLwVUPRGI3Kk6IHVe91vb4vJWFyu",
"JiaCd9hHMpBMvSk9pcf5MtCPVSWmsBTXsnpFMRjfTrrAbysgOKIU07un1KLxbIZk2pFB9G2AHFcl",
"LhH8dR1SJXIVhj9qfx9mkNwV/3P3sRUA782xsJgPyKBKaPFqBeEHtdbVf3w0c//l1uWmQZdMCqkd",
"QDFjX01jXqyizJoAZ4lIlKrSY62+FWE7ka6wJYCiD7jTqhLGQEKWpPx+BzAaB5IeQGuosSVA9dpw",
"pJdZLLvDf5MWQB2tlp2l5rYANElh7WXNJUV9B9LoQCkLoYS88laW1TG1BaA5HGgU5iHhd4hoq4Q5",
"D5h7AX7YjnXHT6hjY14u0+rrv/MU2UVQNQBn3Cs/7EVWUWe71BngPm1HPGl775tCSQ/zRtnEPc96",
"uhhRyhMJGSTuAASXUiNoWyQyRG/k9aNWreKTnhUWTDrZrgcAS+BoAeKCP9f7zXhctHUE0rPCgk8m",
"LE1vZlTOAfIT9RVxBfGD+sE7AEg6f4yabHruLgAinfgOjDAW6l816p43Ys/6RsBTmZbObzjmmACA",
"nmS8l4cA6H7njtb55I96NW7S5WzyYzAHxQC/D/hDLqygF1LnAz+I/O4vOnQN1KLjxr4FmMbNvVLZ",
"XLYIPQFKSfJltiyy08tZJJKRiH04w/MBxDotvbkcBxDlDcA4Jcg4PQBY3FJeH2cextH7Ost65c3T",
"SofC1KMLoq4/kV5Ca8zkgsb/yFSAzSBcHRArRlWy5I+kNBe1qh+Ocwx+ACVi8UI+CKn+7EKVEo9b",
"1UNPJgLIIU16FsUBkkwFEHWXvjgTjawKAGcdYJO3TprBES/QZi8rPCUHEDJAhRSgeGO2uG0cA3C1",
"15a4wztHlJYVYVNMGQCmNG3Se/vSLihhC2K3UTj6r92hM8kKiUfKmh5Kg1STVWTK+rrD7Y+qOpnX",
"B0hTyQShAwjlSXYi0iaFQzBWop8HljQcVonreyEpAAs45IUnxIsjTEWOXAbAACCohoWLjc7Zj/MY",
"CZtmSAHYVJiltAC9Zw2Q9pJ5fY/+50LfEkldJFIlh9cOAGATEgpB6KB/MxntugJ+TQ0QqjKDq0Ut",
"oWVwBZj9PpSLJwxfk8RsgQpgmSvJWlythSdOIcigrCRunNV/uD9jMB0AUZU2tqPqa+Gpgw7KBpWo",
"cfb3JNcHqjUriFO9HVWAHXVwNKweND/83XhAXlJ2QcB+iSp84QBApIFF5+I/PQ5J1pUS4NUPVMSp",
"PUDVeL6nwx5ov31F0md4VK1uGqvmoPwJsQdoE++FZL0JT3l+ZfyJSFmmihnVB6dgD9DOa3LoaXVA",
"+lXbkRYo1xBWvDD/h7tb8LItwJ80XVBAr083x+mOHkAgWTFQq0DAxHLrq7BvrYPHHYCmZGUJ8Dzl",
"aYvbm4HLVGhZAAwcWv/+J+PhU1WrxCUSGSyHieGHO0tdWaUdU/qmfUcF8HSkBBEHcJuTVPRPRVGt",
"/0V7fY8wWrLTSmAEgKsXKQHXCcm/jQDQMMYZyTNVAYRV1EvM2bb3mnViqvaaLJWnAMAe82IBkPXD",
"ukHbGkV3RNkFZUQL7NUFvRBzV72OtW4PVMXKiCSyZWZWAKm+dj1oP260cfZDy7BC01edp+X9euGq",
"DUC7CoJIqry587Qc8yWAmtLxddUnQyKpc991ByCKJ1fqQFusHF97ANgPYEO3fuGJPJIi60SyUM0T",
"QLuE5FwaSR20caO8WGIPYFxQI615kgji/xxfu0n9ALRSiGJZxpZAXMlWWzinp9fNa0gkE83HbNQZ",
"A+BXLwMgkDzWGWXdPYEAaLlmXsZzJk3YIUlk7pGfLMwA8gEW0L2vpNrlZZZhsRkAqSYUr6QTWGG2",
"7gdAdzfgd1NQFm3berxxbog9AW7dkC966tq90kYl0o97AvzowDBwVFFuEx2RVeIHkH/RAFCizMYq",
"RQXEEeBcNTsWwcMjQRjaREc2C+qkEsjli7dwF7MVgc0EpYBr2mBACXBYquO3jNtCjmwq9jlc95JA",
"hd9SRy+i71NELKwgg8gLIFeEEeyWkej7FMBikpJC9K4PAJH6L1bPo2Uo7D/VlV56KaK48BkLFGvo",
"6RE9y+pNAMTKMzcLHh0BKvWFhAQVz5jbDM9V2V/4PREAvET5EgbtnKJxVlsQmHc7WAPUks94zarS",
"F8HbJMsShqkB9uIs4AHDm8b4gFnKV73MUH0hwF7Ecxgs7RIYbaCugLoC5MoLS57u4GX1xFw+ZN2f",
"+gC8pbwwYxkeekyNKygbAJR66MCHatIUUA7xI5rfNA4EIkEdZB4ApRqAsLUU0WfYWhOTD4jEwrnM",
"owsKNSm8CTmsXLMonOaxKD5YbLtyAKhQCjmEkKTGYCTfjzJfgG8pkznMQWQQkDg1SiC9gX0BLlJ1",
"NkmIAqK3sDEW2+XFz9Bin1NiAljqukBUOQAXkTkYjGwBZqVZpUptFYtMtuFl85yAQMSs4PMWO+8i",
"IwBqAMo674p/aJwTPICPVr36HDikqSqilEBepz3DzAjA9oww8foAqBzGCfPEv7SPmB0EFhF5woqt",
"FgChXUDGtHAXKlrBHvPwVfKvBiUQ1YVJAQhT2NpRb0aHpnCQA9htNrMDQLxcUdZ6uRqY7JDOFots",
"BwOiBAj7KhKYSpaUoCOr3ae2E8qAr6Kr6up3eGiIBxGVroYzu8JS31ciHE0BB6a1SxXwNerOALle",
"Wxu+NWMXpMHFU6styFi2lkYJUBgrDS0nftduFziSrTlQgloCsJ567rblLnA7K2xGLbu0JQPYPtiz",
"A4itjGB2w4NhQAQEleX9hwByI0Cd3f+0LkJFxvTUbVuAyEoHg05J+O67yDgp2AQfgNQ4ZuTiiIHI",
"ODG8bguAHYxAAFTa1cGNJLetAe5bjAQdANsBUIBpfXkKcNMWYODWzQBsYmquIDrt/n7Hxg10ALnl",
"zNQeoNukc2QDUPaLO/pldHYNtQAE23RBr/3HJF2A8nbdyLI6jJ0xO1FxPdZZgX2rZcrWsqpPeZHa",
"PZkG4GanN8pS6+yM/NwA4NIFcD1uhyHFZG4ZZm910VrkBACb3UJWlQTSN+QAsRoAOQDgNhbDir7b",
"GE2g7nfFg3nNEJKwVZswt5xEi02eysqV25mC8aEvQBFMAoCPWwBlTmWkbYdCb2isDEodfDEf2sR6",
"85xYppLeEA+6EesWc9o2DpALANtsHql17UgFsOEAsNcCQG6bz+Sr3qpddKRbTWrbbnUAt2wBuNSq",
"LECSow+cAfZx6713rQF452chhNLd5Hu+ALBnl8aotxpnEZHO1FO3gwpvBp333rOMBgRAygAkKX/k",
"BrDe29q3ZTkYi1zhEy64MUDgNBhBCKbxawzQrK4sZW6ydAQISGIAGLub1gvL8PLQDQDxzT31aLhl",
"B1BqAQLXQwKbHWaq03PGgUfVP6BgNBR5AzxQhMWSyKcH0Bhps/XjfUM0siU1MwaAvhZldmNRd9QM",
"A6gXBd6xjMeWlABkVREQaACYJ3rKf3pTqMUTn+MLQ+4J2Z6027b11eNOCdHTuufF85stYEMmgUzs",
"o0mIpQTSXuzzoA9AUg8JYJHcJKqF4BIlrEPyqpVQ1fzHDLAsAyjEEVvynXaSuKcOyYsWoBSCsgkH",
"12UAJfAtFNd237WUQDQDUADcE3tsPQDCFoClue0AehuC6sMluUssLOq1y9K6aHPQnezQYRlAOANA",
"RE8Vodehxcye6p0QshMFJTpQV67SPgA/tSH0sQL2iA955f4mpuU+sQDIe/vy6uUVHKAM7RYOyAZ8",
"5sJ2lq+Pt/vKAMr+xsD6w21NJXA+rRR104lVOzMsUW8KVH94DgDQz2di2fSv95FaG8NmYuwBEKsB",
"iNQKKkgGAO1WZuIHgLUSUALAEOC0OdPLHQBp5zOyVIw4Fab9O08xndQTDA8ASO7Q7Of8AcTpLc3Z",
"IT6+YDnQZFilWZikA4i7Hb7CHHycEdKk96R5qLi7Pq67L2gcQurpjqUAq4q1t3E39M4CBL7uOLPN",
"T4ijc/LOkebthQxgyQsgduuCsndID+4BMLGgf/ABwG4AVa86PwKgu35KkDmYIY3KEQA0AB/3uT98",
"QjmlkCaCjtJeRFUIp/20Bjj2evEGcuoCetYP6WoJ1ACQ+r35w00CGV9b1gcgcKu2zX+64QXAz8ze",
"JpY6kPdjylxUePfrQ7zR214AvCh7M7WsGKVd5BVwcSB2zKIA2PB7uQiv2GSFJUDeedxWe26FIki6",
"ftMLgNUrXnxcWuxNrcPStrcagEzYw8YcAOyQzPVdY6KS+eKkU9oWQMhiI9rwA2Bz9Ce0GK19QKYC",
"aQOQsjVkABvhm34AOUByKkv3mwCaM+dTcUz1Xuj3/p2PFADJQ1mSJTEBkAaAv+Fi9/nvegHAAT7m",
"ye/MIi4fAny91gGxbICsrPgBxPEDeZopNhTHdjbrMjA/4iJwWMAxc59CJP9/YFW57SUjtmuAgufg",
"VgDtgKcW/rG86hUbAK5HDUAzOPgBQLM6wU4CPWupdxQdtNd7AtyI5SsgsBFAfKJxgJuJfs+5Oosa",
"y5Odoak6xwG26rU7y7CReEqgkXVm5QrzkQTaTVpf9gXAirKXJYBYAbEJG/tzAhQyMxjVqvNR5k1k",
"qHZ6Zw55AowMkZ8wGBgXaohkd0yM+wyNALNqiGUvuxppCgkLmHm3izdANmsGh/kIYNZWUCleaDAJ",
"wIwaCh9nWi9VBeJQaDIBwKwa7vUdpWqhhgDAkwCUFumycfh+WMLGzPE7vgDyLznSL9SIjnkh8xIB",
"7uoXauCsBJt9lmZXrALA+oUaIT91bq4Xc8YOAONc3GFxW3q+qGPKVrMWybAANbiIm6PglucGIEYA",
"yRX0LiAxPfSbmfR9XapVEUUnJSdE5Bs3ngGARNfXknyXL8dIb2z7AYSGLoj0q5VW4pzPI9+h2c05",
"3YAFgCwhHfOT0auQptvzAlS7JgDZkBfzAm657KWEq4MbXJSmsUCmJLhiOsBOYPHIkbgCyPoI81Nc",
"mAR+BTkHBWsWfZwYVk2GlAOs32ZV6L15PDFlb9PU/l3qb3+TEpTQYrOq31s4D8ATqldCBcDXgR1g",
"lsNGUL5E5gKQdnJkWLSI6I8goVmU3oSXSlY52ro8gFwO8F0OwBTwt306YTD1+I7WUSnW21Q3EprF",
"7I/fiZil/oK3J5R2c2haPJ3QG6/QbDsHnFW8lIXmARh3QmAEKCE4yrZzdJKLwiYi/jogEbNptOQH",
"fwRvrefoe3zBrXPtyLj3zrSLg214XoJ1Njl+SXzB1jwAheYCxXEt/CPrB9CsKkDfmgeg1PSR/AsC",
"AfCpbkKbzgOgyVjKh+t6Dnl9DZqlMcFcSjhWgti0fl28qWKtURh4eWKAyATQZSZ+kcnzHZulXZou",
"cAfo6tYr7HHyfXAalSPTMnkzQNEEY8v1CoOtOTyh5NBc0y4OTKtbDYBYKfrL3oORDCA0AaCe6UT8",
"58AFIJgbAOpKElnhGpU7ztImAKhfHsZmp5TeYZ7ZJT5GJoDADBDRvzgMWd0CPaTcLThNlCcAgOe+",
"c/o+3zKFxOvsnN5EbvIDFl3A87aigkLhNrPCXX9PpAEg2inGfuMKvgGwtOUPkKm7QAew2e6Wib4H",
"buNRbNg1F1htqFtpFhYod6/ZAmiCQstvvmU4r1c/GFQaO9UCLI1mnc8YwOPOUoByDoBPpALAsXSB",
"7QEMLv7mvmwJteNwWOgATHXB5Is+FeTAMC8YWME1fWcWFic0GgAyjYRywxEDsR8AMuzexfMBLDsC",
"EI2V5IYzDhKajV87jqYF0KXl04S+b3Uk0yUBBBnbbxR5ZIkmAsA5PybUZ32xHiDuAQSV3qGlozcZ",
"TZCm6gPo1mnE3Ijbc0pveA4GOoAM8LkeoLA4KNXgi3UHV6faN9vHfCxtC3gbnmZAdACBZoc3qpef",
"l70585KHFuoAQAewXO/GKoexsrMSaADKZimtPDCvjyzxqSBiO4BcC1BvwxqEVFseWqgBSLUAWDJ9",
"sXVKgbYmEXdslwUA2pJA3H2zuQtcD6OwAYi6PwWaTSnxLMDuVAC4C1cDm10x6dQSwF24Gmgic9+T",
"CGYASrWNZFqAwPM4DA+ADcOQWrkOBQOAQv1sGWNJzeOJALi26weQqwFyAbBsjCk85oiJDgB1f8I0",
"hRW78Sz07IJM/decuQSb/RDitY1ksi4YABCL2ZW4JvMDSNXiLTQA0Xh6ueUHQAwANmGle1TY2dAP",
"QAuALx2ALBighCkASn+A3ARQWaX6/AFSX4DI+5y8IQDRPV4meYeuHCD1BtDKl5enrDJt3/cFKHUA",
"/I+pFUB2KQCFfaovvzwJqNonLxUgMgPE8wEEcwNEiwbA0wBUum8vLhEg1EsgvHQrmBqg8AUodEaS",
"2ye8vQG05+SlzwBAuweZ2Kd7vXUg1U0bFg4AlwhQW7HqTQrPDOCCaHOg9hn3zBdAN9SUzwAg1w01",
"hUPKf3IAbO7XpP/y6MzTCrQAmRkgg9gzKA0N4Nj8peKIlNATABsAIvNkI6lzSH4TE2wAj8xnj4mz",
"cYLK/djaHoDu+OzCHFWyj3sCROYTzIkJoOJl86d+k1MO8J4aPDA+E6pN6PU5AB5qykFmta4PA/hC",
"/5wp+1xl7JdWGDxDbdB9gOVnCdB3SI1DC50+NQ1A+Pk+AHn2ACt9r22fq6R+1qvy6q1HXQRA4AOA",
"LksCt5ymhuX0Eth3ksA0Zoj6Y8rtWy4AOUzkCLovSnIXgHQagPCkCyuSzAFgGhWAwRYX64eazAZm",
"FXLXXgKXAYAoLAJgtxsEEwfbnVACHcDOYgDatmW7CzbkLwqeqm22t/2DfXsJTCiAbsdfYj0WJJcD",
"YD1BWZkUoC1qoMoBYJ5TPGYBmlENlS4Ae9MDBNYA6wmd5xSPWY1qY1L78XVSK2hnMeJI+F07gPIS",
"/FCyIAk4D0UAz1+KBMLKBaCYHgDbA3xksnhsMF21B8BTWmE347Xv1ojSSzACh+W18aUAfNbeCpNL",
"6QKHzb/hpSihw4Y7fClmCD9DAJ9eNMCk8YhPe9XdClanloDrR1YntdtfdwfA0zrNS5kXObTXLiUg",
"chk4p0rP/AwDLNYNwCeZBFYWCPFZvmI3XRzAi2wo+NgCuyCgf7tYHXjup4sdDGHpYMGOKCh3FgsA",
"P+y9V2t5EQDP3+ucQEAWAID7o9Ei/AFK3lqwEqzBVbtqV+2qXbWrdtWu2lX7P9D2/r8LYHXhBGh3",
"wQC/seD7v7DgPNFS/PZiAX71y8uLBQgX3APjk5x+dgCCaZyAdb58vC7ittNaBZUKXNgmRpLZ2gJi",
"Z4GJNFtceoNEtm9FQ8MaY3TGNybErOZzn/ivClw5sr3yZr+68kJ/s3VEM/+Fkeu2KrDW32zR2+H2",
"dI4tNqxd+7blhTv3ejeRv/3uvlcfWL4OKupvsI8U7//z6oPbdpf1zxpRvQCRFp/yeM2exZKbgPSe",
"udQA0ItT12RnRCwAwqzX6aXqxWt+Wy5fPbEoXYb58LyUWAPg6A5uRKfa0qXo0rBvawV8Rnd/x0po",
"hh+rJMD2qayt8JPmBk+s0QAfUwieUip5R+cSAvgaLiNarVybMbriowaAU5eRsAxKKXIckHrvQ1TN",
"HCxdYAPAEydFTKQA13fUmpZF1NgcghEqAwhpTC9UX04sAIwiePz3DcCdMpGsodA+nfn+ZlPshPRq",
"kUgGMd2XBxYAZt/2z1t1oYKmklFUp2ZVPD/A8jY727dgps5fyVS4CKBMLAAUgUH7inihRm9DAvw1",
"jl8caeacTaWDQfPqKSxGkywpXpHxJnPe/5yojmSK4x9c2x+f2j80w+vawW4eAeytwSYixfaok4cS",
"WEPz9kCpUIAtzLxqvj0KqUZuaL5WyTvgbs9EtkKdykbzAihmNdZGG12CCvye/cgxtw3KJPBruuvJ",
"pI9vOH/dGL9gehkALxwmtt1FpwcIonRpL7GEjacAoENHGPAptJ0GBpPcn6YJsXQrpx0Az5PgaQBy",
"+24lPe3PpuoB+mTP0rAe8oVkpHZU2fzDoMS3BoWmD8rejqDEOOXy64GQEo0bOCoAP2w6qmCHw0+B",
"QEaTb40eAiZFDVDytPEEAC4j0Yf87ekHnJGDpBNYwvj+WCuuVfGiSDqVJyzBZSxIMaD77ETsfKKR",
"SHaiR2QJjCYyQjKT3jXpVTFVMKzMDyR2xFMJYKwEiYUEIlqEUwFQl5le/YGY0vPJ7l+1mcddyyAD",
"pnv4oTNmrzzILEw7jScGqCYOMOZQg414UQTZRNOs+bohoovexMPtK4WrdtWu2lW7alftqi2m/Q9h",
"A4fsM4eleAAAAABJRU5ErkJggg==",
].join("")
export { GLOBE_LAND_TEXTURE }
Installation
npx shadcn@latest add https://shaderscn.com/r/dithering-globe-arcs-hero.jsonUsage
import { DitheringGlobeArcsHero } from "@/components/dithering-globe-arcs-hero"
export default function Page() {
return (
<DitheringGlobeArcsHero
arcs={[
{ from: [37.77, -122.42], to: [51.51, -0.13] }, // San Francisco → London
{ from: [51.51, -0.13], to: [35.68, 139.69] }, // London → Tokyo
]}
/>
)
}