SOURCE / PINNED RELEASE
Made of little things.
Drone Zone — deep field
- Release
- 578f277a8be1…
- Author-recorded commit
- a96742b428de…
- License
- LICENSE
- Author’s source reference
- nostr://npub1n8ga89w8h6tvwamxusfyzexw8gjy84yxu9rxgnmk955cxtml4ujswzxydd/wss%3A%2F%2Fgit.napplet.soy%2F/n-9753e6cf3ec
Archive hash verified: f7f3bf8ff3482551…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
export interface Point { x: number; y: number; z: number }
export interface Observation { name: string; value: string; description: string; x: number; y: number; z: number; label: string }
export const observations: Observation[] = [
{ name:'THE HYDROGEN LINE', value:'1 420.4058 MHz', description:'Neutral hydrogen whispers at a wavelength of roughly 21 centimetres. Radio astronomers use this line to trace the structure and motion of interstellar gas.',x:310,y:-165,z:130,label:'H I / REST FREQUENCY' },
{ name:'A DISTANCE MADE OF TIME', value:'299 792.458 km/s', description:'The speed of light in vacuum. Looking farther into space means looking farther into the past. This beacon is a place to pause, not a measured star.',x:-410,y:105,z:80,label:'c / VACUUM CONSTANT' },
{ name:'ONE ASTRONOMICAL UNIT', value:'149 597 870.7 km', description:'The astronomical unit is an exact length, originally based on the Earth–Sun distance. Sunlight takes about 8 minutes 19 seconds to cross it.',x:640,y:295,z:180,label:'AU / REFERENCE DISTANCE' },
{ name:'THE OLDEST AFTERGLOW', value:'≈ 2.725 K', description:'The cosmic microwave background fills space with the cooled afterglow of the early universe. Almost uniform. Never quite empty.',x:-670,y:-280,z:110,label:'CMB / MEAN TEMPERATURE' },
{ name:'THE PALE BLUE INTERVAL', value:'≈ 8 min 19 s', description:'The light reaching Earth from the Sun left about eight minutes ago. Even the most familiar daylight arrives with a little history.',x:65,y:345,z:90,label:'SOL → TERRA / LIGHT TIME' },
{ name:'A YEAR OF LIGHT', value:'≈ 9.461 × 10¹² km', description:'A light-year measures distance, not time: the distance light travels in one Julian year. Our drawn trajectories are an imagined atlas.',x:735,y:-340,z:150,label:'LY / DISTANCE UNIT' },
{ name:'THE LISTENING POST', value:'DRONE ZONE', description:'A receiver in an imagined field. The linked Nostr station supplies the real radio stream. The moving signal traces are generative artwork, not an audio spectrum.',x:-170,y:-340,z:200,label:'RX / AMBIENT TRANSMISSION' },
];
observations.push(
{ name:'THE LONG WAY HOME', value:'H01 / OUTER APPROACH', description:'A slow lane through the outer field. All its travelers follow the same curve, separated only by time. This is the beginning of our imagined highway.', x:-1700,y:510,z:90,label:'H01 / OUTER APPROACH' },
{ name:'THE LAGRANGE GARDEN', value:'05 / QUIET ORBITS', description:'Five nested ellipses turn around an empty focus. An imagined garden of balanced paths, inspired by the places where gravity and orbital motion find an equilibrium.',x:-1060,y:-640,z:140,label:'L05 / ORBITAL GARDEN' },
{ name:'THE SIGNAL THAT RETURNED', value:'ECHO / +0042.8 s', description:'An artificial pulse leaves this recorder, circles the field and returns. The displayed interval belongs to our simulation. Nobody is waiting for an answer.',x:1220,y:-690,z:170,label:'E07 / ECHO RECORDER' },
{ name:'A SMALL INTERRUPTION', value:'CARRIER / ABSENT', description:'The route continues on either side, but a piece is missing. A small flash marks each departure, then the traveler accelerates through the silence and returns to its unhurried pace.',x:920,y:817,z:210,label:'GAP / CARRIER ABSENT' },
{ name:'THE SPECTRAL ARCHIVE', value:'λ / 380–750 nm', description:'A sketch of the visible spectrum, approximately violet through red. The drawn traces are a generative score, not spectral measurements from this image.',x:-1410,y:980,z:130,label:'SP / OPTICAL ARCHIVE' },
{ name:'THE FAR RECEIVER', value:'RX 12 / LISTENING', description:'A receiver at the edge of the atlas. Its little aerial follows no source in particular. Sometimes listening is the entire experiment.',x:1830,y:720,z:180,label:'RX12 / FAR RECEIVER' },
{ name:'THE PARALLAX ROOM', value:'01 / TWO PERSPECTIVES', description:'Move the field and watch the foreground marks slide against the distant light. This small difference in viewpoint is the principle behind parallax.',x:1640,y:-80,z:230,label:'PX / TWO PERSPECTIVES' },
{ name:'THE SLOW INTERCHANGE', value:'H01 × H03', description:'Routes fold over one another at different depths. None of the apparent crossings is a collision. Our imagined traffic has all the time in the universe.',x:-910,y:180,z:150,label:'J03 / SLOW INTERCHANGE' },
{ name:'AN EMPTY FREQUENCY', value:'∅ / OPEN CHANNEL', description:'There is no station here. Only a drawn horizon and a quiet frequency waiting for something to cross it. An intentional empty room in the artwork.',x:180,y:-1070,z:110,label:'F00 / OPEN CHANNEL' },
{ name:'THE DUST LIBRARY', value:'ARCHIVE / 016', description:'A small optical library of supplied space images. Its windows collect different glimpses of distant light, rather than measurements of this imagined field.',x:-2040,y:-790,z:100,label:'D16 / DUST LIBRARY' },
{ name:'ONE MORE HORIZON', value:'FIELD / CONTINUES', description:'The edge of the chart is a choice, not the edge of space. Beyond this marker, the route thins and the foreground stars keep drifting.',x:2090,y:-970,z:160,label:'HZ / OUTER HORIZON' },
{ name:'THE PATIENCE ENGINE', value:'00:00 / NO ARRIVAL', description:'A machine built to keep moving without needing to arrive. Its concentric traces make a soft clock whose only unit is attention.',x:520,y:1120,z:180,label:'T18 / PATIENCE ENGINE' },
{ name:'THE LAST LANTERN', value:'BEACON / 019', description:'One warm point at the southern margin. The highways carry on beyond the chart. The light does too.',x:-610,y:1310,z:200,label:'B19 / LAST LANTERN' },
);
export const navigation = { minZoom: .55, maxZoom: 3.6, panX: 2400, panY: 1550 };
export const tourRange = { center: .5, radius: .09 };
export interface Route {
samples: Point[];
color: string;
phase: number;
speed: number;
weight: number;
opacity: number;
gaps: [number, number][];
label: string;
destination?: number;
}
// Each family has ONE broad change in curvature. Lanes share the shape;
// no independently perturbed waypoints, terminal spurs or local highway loops.
const families = [
{ y: 120, slope: -.1, bend: -260, center: 220, width: 1150, lanes: 30 },
{ y: -360, slope: .13, bend: 190, center: -400, width: 1600, lanes: 20 },
{ y: 850, slope: -.045, bend: 130, center: 850, width: 1350, lanes: 14 },
];
function highway(family: number, lane: number): Point[] {
const f = families[family];
const dense = Array.from({ length: 4097 }, (_, i) => {
const x = -10000 + i / 4096 * 20000;
const spread = lane * 9;
return { x, y: f.y + f.slope*x + f.bend*Math.tanh((x-f.center)/f.width)
+ spread*(1+.13*Math.tanh((x-f.center)/f.width)), z: 100+family*55+lane*1.5 };
});
// Equidistant, fine samples keep traffic speed constant. Maximum chord error
// stays subpixel even at maximum zoom; the display never decimates these.
const distances = [0];
for(let i=1;i<dense.length;i++) distances.push(distances[i-1]+Math.hypot(dense[i].x-dense[i-1].x,dense[i].y-dense[i-1].y));
let cursor=1;
return Array.from({length:2049},(_,i)=>{
const target=i/2048*distances[distances.length-1];
while(cursor<dense.length-1&&distances[cursor]<target)cursor++;
const a=dense[cursor-1],b=dense[cursor],t=(target-distances[cursor-1])/(distances[cursor]-distances[cursor-1]);
return {x:a.x+(b.x-a.x)*t,y:a.y+(b.y-a.y)*t,z:a.z+(b.z-a.z)*t};
});
}
export const principalHighway = highway(0,0);
export function sampleRoute(samples: Point[], t: number): Point {
const f = Math.max(0, Math.min(1, t)) * (samples.length - 1);
const index = Math.min(Math.floor(f), samples.length - 2), a = samples[index], b = samples[index+1];
return { x:a.x+(b.x-a.x)*(f-index), y:a.y+(b.y-a.y)*(f-index), z:a.z+(b.z-a.z)*(f-index) };
}
export function createRoutes(): Route[] {
let seed=71821;
const random=()=>{seed=(seed*1664525+1013904223)>>>0;return seed/4294967296;};
const colors=['117,193,217','100,167,197','221,135,89','157,188,208','106,149,217','176,204,209'];
const routes:Route[]=[{samples:principalHighway,color:'173,211,224',phase:.451,speed:.0016,weight:1.65,opacity:.58,gaps:[[.468,.476]],label:'H01 / LONGITUDE HIGHWAY'}];
families.forEach((f,family)=>{
for(let i=0;i<f.lanes;i++) {
const start=family===1&&i===8?.522:family===2&&i===7?.542:0;
const gaps:[number,number][]=start?[[start,start+(family===1?.009:.008)*(3+random())]]:[];
routes.push({samples:highway(family,(i-(f.lanes-1)/2)),color:colors[i%6],phase:gaps.length?gaps[0][0]-.018:.36+random()*.28,
speed:.0013+random()*.0008,weight:i%10===0?1.2:i%4===0?.8:.52,opacity:i%10===0?.38:i%4===0?.24:.15,
gaps,label:`H0${family+1} / ${String(i+1).padStart(2,'0')}`});
}
});
return routes;
}
export interface Transit { t: number; flash: number; entry: number; warping: boolean }
export function createTransit(route: Route): Transit { return {t:route.phase,flash:0,entry:0,warping:false}; }
/** Integrate at gap boundaries, so even a long frame cannot miss an entry or
* spend its entire duration at warp speed after crossing an exit. */
export function advanceTransit(state: Transit, route: Route, seconds: number): void {
let remaining=Math.max(0,seconds);
state.flash=Math.max(0,state.flash-remaining);
while(remaining>1e-9) {
const gap=route.gaps.find(([start,end])=>state.t>=start&&state.t<end);
const next=route.gaps.find(([start])=>start>state.t);
const boundary=gap?gap[1]:next?next[0]:1;
const speed=route.speed*(gap?22:1),duration=(boundary-state.t)/speed;
if(remaining<duration) {state.t+=remaining*speed;break;}
remaining-=duration;state.t=boundary;
if(!gap&&next) {state.entry=boundary;state.flash=Math.max(0,.34-remaining);}
if(state.t>=1)state.t=0;
}
state.warping=route.gaps.some(([start,end])=>state.t>=start&&state.t<end);
}
export interface Orbit {
center: Point; radius: number; eccentricity: number;
inclination: number; ascending: number; phase: number; speed: number; warm: boolean;
}
export interface PlanetarySystem { index: number; orbits: Orbit[] }
export function createSystems(): PlanetarySystem[] {
return [8,9,12,14,18,19].map(index=>({index,orbits:Array.from({length:index===8?5:3},(_,ring)=>({
center:observations[index],radius:42+ring*24+(index%3)*5,eccentricity:.84+ring*.025,
inclination:.85+(index%4)*.12+ring*.025,ascending:index*.63+ring*.035,
phase:index+ring*2.4,speed:.045/(1+ring*.5),warm:ring===2,
}))}));
}
/** Analytic closed ellipse, rotated in 3D. Bodies and orbit lines use the same
* function; there is no duplicated seam vertex or spline closure to kink. */
export function orbitPoint(orbit: Orbit, angle: number): Point {
const x=Math.cos(angle)*orbit.radius,y=Math.sin(angle)*orbit.radius*orbit.eccentricity;
const cy=Math.cos(orbit.inclination),sy=Math.sin(orbit.inclination);
const ca=Math.cos(orbit.ascending),sa=Math.sin(orbit.ascending);
return {x:orbit.center.x+x*ca-y*cy*sa,y:orbit.center.y+x*sa+y*cy*ca,z:orbit.center.z+y*sy};
}
export interface Camera {
width:number; height:number; scale:number; pan:{x:number;y:number};
cy:number; sy:number; cp:number; sp:number;
}
export function createCamera(width:number,height:number,pan:{x:number;y:number},look:{x:number;y:number},zoom:number,motion:number): Camera {
const yaw=look.x*.06*motion+pan.x/navigation.panX*.025;
const pitch=look.y*.045*motion+pan.y/navigation.panY*.018;
return {width,height,scale:Math.max(width/1700,height/1080,.31)*zoom,pan,
cy:Math.cos(yaw),sy:Math.sin(yaw),cp:Math.cos(pitch),sp:Math.sin(pitch)};
}
export function projectPoint(p:Point,c:Camera): {x:number;y:number;depth:number} {
const x=p.x-c.pan.x,y=p.y-c.pan.y;
const rx=x*c.cy+p.z*c.sy,rz=p.z*c.cy-x*c.sy;
const ry=y*c.cp-rz*c.sp,z=y*c.sp+rz*c.cp;
const depth=2800/(2800-z);
return {x:c.width*.5+rx*c.scale*depth,y:c.height*.5+ry*c.scale*depth,depth:z};
}
export interface Detour {
route: Route; orbit: Orbit; source: Point[]; system: number;
first: number; last: number; width: number;
}
/** Original highways are monotonic in x. Find the precise material coordinate
* of a rendezvous so traffic and gap markers retain their identities as it moves. */
export function routeAtX(samples: Point[], x: number): number {
let low=0,high=samples.length-1;
while(low<high){const middle=(low+high)>>>1;if(samples[middle].x<x)low=middle+1;else high=middle;}
const i=Math.max(1,low),a=samples[i-1],b=samples[i];
return (i-1+Math.max(0,Math.min(1,(x-a.x)/(b.x-a.x))))/(samples.length-1);
}
/** Reroute one existing lane per system. Preserve the principal tour lane and
* existing gates; choose the nearest available lane in the actual 3D field. */
export function createDetours(routes: Route[], systems: PlanetarySystem[]): Detour[] {
return systems.map((system,ordinal)=>{
const orbit=system.orbits[system.orbits.length-2],center=orbit.center;
let route=routes[1],distance=Infinity;
for(const candidate of routes) {
if(candidate===routes[0]||candidate.destination!==undefined||candidate.gaps.length)continue;
for(let i=0;i<candidate.samples.length;i++) {
const p=candidate.samples[i],d=Math.hypot(p.x-center.x,p.y-center.y,p.z-center.z);
if(d<distance){distance=d;route=candidate;}
}
}
const source=Array.from({length:4097},(_,i)=>sampleRoute(route.samples,i/4096)),lastSample=source.length-1;
const width=Math.max(520,distance*1.8+orbit.radius*2.5);
const first=Math.max(0,Math.floor(routeAtX(source,center.x-width-orbit.radius)*lastSample)-1);
const last=Math.min(lastSample,Math.ceil(routeAtX(source,center.x+width+orbit.radius)*lastSample)+1);
route.destination=system.index;
route.samples=source.map(p=>({...p}));
route.weight=Math.max(.95,route.weight);route.opacity=Math.max(.44,route.opacity);
route.phase=routeAtX(source,center.x-width*.8);
if(ordinal%2===0) {
const seed=Math.sin(system.index*127.1)*43758.5453,variation=seed-Math.floor(seed);
const entry=center.x-width*.84,exit=entry+width*.08*(3+variation);
route.gaps=[[routeAtX(source,entry),routeAtX(source,exit)]];
route.phase=route.gaps[0][0]-.012;
}
const detour={route,orbit,source,system:system.index,first,last,width};
updateDetour(detour,0);return detour;
});
}
/** Compact C2 deformation: position, tangent and curvature meet the untouched
* highway at both shoulders. A single broad bend follows the orbiting body in
* x/y/z; it cannot grow additional loops or expose a new endpoint. */
export function updateDetour(detour: Detour, time: number): void {
const {route,source,orbit,first,last,width}=detour;
const body=orbitPoint(orbit,orbit.phase+time*orbit.speed);
const anchor=sampleRoute(source,routeAtX(source,body.x));
for(let i=first;i<=last;i++) {
const base=source[i],p=route.samples[i],u=(base.x-body.x)/width;
const blend=Math.abs(u)<1?Math.pow(1-u*u,3):0;
p.y=base.y+(body.y-anchor.y)*blend;p.z=base.z+(body.z-anchor.z)*blend;
}
}