fp64 hyperbolic navigation
A LORAN-style chart grid: cyan hyperbolae of constant d₁−d₂ to two stations, amber ellipses of constant d₁+d₂. The signal is the DIFFERENCE of two nearly-equal distances followed by fract() — catastrophic cancellation plus phase recovery. Drag the DISTANCE slider to push the whole station triangle out from the origin: near 10⁶ both halves are a clean chart, but past ~10⁷·² the coordinate ulp grows wider than a band, d₁ and d₂ quantize, and the plain-f32 left half dissolves into blocky garbage — while the vec64 distance reduction keeps the right half sharp to 10⁹. Wheel drives the distance; flip the fp64 toggle to compare.
One source, compiled every way
The DSL source below, compiled to WebGPU (WGSL), WebGL2 (GLSL ES 3.00 — two stages), and a reflection JSON the host binds from. Nothing but the DSL tab is hand-written.
// ═══ @xgis/shader-dsl example — fp64 hyperbolic navigation (LORAN) ═══//// Hyperbolic radio navigation, the pre-GPS chart grid: two stations, and your// position line is "d₁ − d₂ = const" — a hyperbola. The catch for GPU floats:// the observer is ~10⁷ units from both stations, the usable signal is the// DIFFERENCE of two nearly-equal distances (catastrophic cancellation), and// the band phase needs fract() of a coordinate-scale value — three classic// f32 killers in one formula. The f64 side runs the whole chain through the// vec64 `distance` reduction (extended-precision accumulation) and a df64// fract; the plain-f32 left half renders quantized band garbage at ANY zoom// (CPU-verified: 17 clean band values vs 6 garbage ones per 48² window).//// The `_fp64` guard uniform is auto-injected by the lowering; the render// harnesses bind it to 1.0f by probing the program for the Fp64Guard block.
import { fn, module, vec2, vec3, vec4, f32, pow, fract, min, mix, length, distance, smoothstep, fwidth, toF32, toF64, f32T, vec2fT, vec2f64T, vec2f64, Let, uniformStruct,} from '../src/index.ts'import { VsOut, vs } from './_fullscreen.ts'import type { ShaderExample } from './_shared.ts'
// The observer and both stations are host-supplied df64 uniforms swept out// from the origin together via the DISTANCE slider (see controls below), so// there are no hard-coded station coordinates any more — only the band pitches.const LAMBDA_H = 4 // hyperbolic band spacing (world units of d₁−d₂)const LAMBDA_E = 16 // elliptic band spacing (world units of d₁+d₂)
const U = uniformStruct( 'Uniforms', { group: 0, binding: 0, as: 'u' }, { center: vec2f64T, // observer — one DF64Vec2 slot [hi.x, hi.y, lo.x, lo.y] st_a: vec2f64T, // master station (swept with the observer via the DISTANCE slider) st_b: vec2f64T, // secondary station resolution: vec2fT, zoom_exp: f32T, // view span = 10^-zoom_exp world units (negative = zoom out) fp64: f32T, // toggle: 1 = split-screen f32 | f64 (canonical), 0 = all-f32 },)
const fsLoran = fn( 'fs_loran', { vo: VsOut }, (p) => { const span = Let(pow(f32(10.0), U.field.zoom_exp.neg())) const half = Let(p.vo.uv.x.mul(2.0)) const sx = Let(half.sub(p.vo.uv.x.lt(0.5).select(0.0, 1.0))) const dx = Let(sx.sub(0.5).mul(span)) const dy = Let( p.vo.uv.y.sub(0.5).mul(span).mul(U.field.resolution.y.div(U.field.resolution.x).mul(2.0)), )
const isF32 = Let(p.vo.uv.x.lt(0.5).or(U.field.fp64.lt(0.5))) // f64 chain — vec64 distance accumulates through the scalar df64 chain, // the subtraction cancels EXACTLY, and only the band phase narrows. const pos = Let(vec2f64(U.field.center.x.add(toF64(dx)), U.field.center.y.add(toF64(dy)))) const d1 = Let(distance(pos, U.field.st_a)) const d2 = Let(distance(pos, U.field.st_b)) const th64 = Let(toF32(fract(d1.sub(d2).mul(1 / LAMBDA_H)))) const te64 = Let(toF32(fract(d1.add(d2).mul(1 / LAMBDA_E)))) // f32 twin — SAME formulas, everything narrowed first: once the coordinate // ulp exceeds a band, d₁, d₂ quantize and the band phase becomes garbage. const pos32 = Let(vec2(toF32(U.field.center.x).add(dx), toF32(U.field.center.y).add(dy))) const d1f = Let(length(pos32.sub(vec2(toF32(U.field.st_a.x), toF32(U.field.st_a.y))))) const d2f = Let(length(pos32.sub(vec2(toF32(U.field.st_b.x), toF32(U.field.st_b.y))))) const th32 = Let(fract(d1f.sub(d2f).mul(1 / LAMBDA_H))) const te32 = Let(fract(d1f.add(d2f).mul(1 / LAMBDA_E)))
const th = Let(isF32.select(th32, th64)) const te = Let(isF32.select(te32, te64))
// Distance to the nearest band line (triangle fold — continuous across the // fract seam, which is exactly where the line sits). const dh = Let(min(th, f32(1).sub(th))) const de = Let(min(te, f32(1).sub(te))) const aaH = Let(fwidth(dh).mul(1.2).add(1e-4)) const aaE = Let(fwidth(de).mul(1.2).add(1e-4)) const lineH = Let(f32(1).sub(smoothstep(f32(0), aaH, dh))) const lineE = Let(f32(1).sub(smoothstep(f32(0), aaE, de)))
// Chart styling: deep sea, cyan hyperbolae (the position lines), faint // amber ellipses (the range net), a soft band tint to keep the field alive. const sea = Let(mix(vec3(0.02, 0.07, 0.13), vec3(0.04, 0.12, 0.2), p.vo.uv.y)) const rgb = sea .add(vec3(0.0, 0.06, 0.08).mul(th)) // band tint .add(vec3(0.25, 0.95, 0.95).mul(lineH.mul(0.9))) .add(vec3(0.95, 0.7, 0.25).mul(lineE.mul(0.35))) return vec4(rgb, f32(1)) }, { stage: 'fragment', retAttr: '@location(0)' },)
// `_fp64` guard lands at (group 0, binding 1) automatically.const fp64LoranModule = module({ funcs: [vs, fsLoran], uses: [U, VsOut],})
export const fp64Loran: ShaderExample = { id: 'fp64-loran', title: 'fp64 hyperbolic navigation', blurb: 'A LORAN-style chart grid: cyan hyperbolae of constant d₁−d₂ to two stations, amber ellipses of constant d₁+d₂. The signal is the DIFFERENCE of two nearly-equal distances followed by fract() — catastrophic cancellation plus phase recovery. Drag the DISTANCE slider to push the whole station triangle out from the origin: near 10⁶ both halves are a clean chart, but past ~10⁷·² the coordinate ulp grows wider than a band, d₁ and d₂ quantize, and the plain-f32 left half dissolves into blocky garbage — while the vec64 distance reduction keeps the right half sharp to 10⁹. Wheel drives the distance; flip the fp64 toggle to compare.', category: 'cartographic', file: 'fp64-loran.ts', module: fp64LoranModule, renderable: true, splitLabels: ['f32', 'f64 (emulated)'], controls: { // Observer and both stations sweep together: coord = base·10^mag (+offset for // the observer's sub-unit detail). At mag = 7 this is the classic ~10⁷ view. center: { kind: 'logmag2d', magField: 'mag', base: [2.31, 3.07], offset: [0.13, 0.57] }, st_a: { kind: 'logmag2d', magField: 'mag', base: [1.2, 3.4], offset: [0, 0] }, st_b: { kind: 'logmag2d', magField: 'mag', base: [1.9, 2.6], offset: [0, 0] }, resolution: { kind: 'resolution' }, mag: { kind: 'slider', label: 'Distance from origin 10^x', min: 4, max: 9, step: 0.02, value: 6, wheel: true, }, // View span across each half (10^-zoom_exp world units ≈ number of bands). zoom_exp: { kind: 'slider', label: 'Zoom 10^-x', min: -3.0, max: 3.0, step: 0.05, value: -1.6 }, fp64: { kind: 'toggle', label: 'fp64 emulation', value: true }, },}struct Uniforms { center: DF64Vec2, st_a: DF64Vec2, st_b: DF64Vec2, resolution: vec2<f32>, zoom_exp: f32, fp64: f32,}
struct VsOut { @builtin(position) pos: vec4<f32>, @location(0) uv: vec2<f32>,}
struct DF64Vec2 { hi: vec2<f32>, lo: vec2<f32>,}
@group(0) @binding(0) var<uniform> u: Uniforms;@group(0) @binding(1) var _fp64: texture_2d<f32>;
@vertexfn vs(@builtin(vertex_index) vi: u32) -> VsOut { let _cse0 = ((f32((vi & 1u)) * 4.0) - 1.0); let _cse1 = ((f32((vi >> 1u)) * 4.0) - 1.0); return VsOut(vec4<f32>(_cse0, _cse1, 0.0, 1.0), vec2<f32>(((_cse0 * 0.5) + 0.5), ((_cse1 * 0.5) + 0.5)));}
@fragmentfn fs_loran(vo: VsOut) -> @location(0) vec4<f32> { let _cse7 = vec2<f32>(u.st_a.hi.x, u.st_a.lo.x); let _cse8 = vec2<f32>(0.0, 0.0); let _cse9 = vec2<f32>(u.st_a.hi.y, u.st_a.lo.y); let _cse10 = vec2<f32>(u.st_b.hi.x, u.st_b.lo.x); let _cse11 = vec2<f32>(u.st_b.hi.y, u.st_b.lo.y); let _cse0 = (vo.uv.x < 0.5); let _cse1 = vec2<f32>(u.center.hi.x, u.center.lo.x); let _cse2 = vec2<f32>(u.center.hi.y, u.center.lo.y); let _cse3 = df64_add(_cse7, _cse8); let _cse4 = df64_add(_cse9, _cse8); let _cse5 = df64_add(_cse10, _cse8); let _cse6 = df64_add(_cse11, _cse8); let _v0 = pow(10.0, (-u.zoom_exp)); let _v1 = (vo.uv.x * 2.0); let _v2 = (_v1 - select(1.0, 0.0, _cse0)); let _v3 = ((_v2 - 0.5) * _v0); let _v4 = (((vo.uv.y - 0.5) * _v0) * ((u.resolution.y / u.resolution.x) * 2.0)); let _v5 = (_cse0 || (u.fp64 < 0.5)); let _lc0 = df64_add(_cse1, vec2<f32>(_v3, 0.0)); let _lc1 = df64_add(_cse2, vec2<f32>(_v4, 0.0)); let _v6 = DF64Vec2(vec2<f32>(_lc0.x, _lc1.x), vec2<f32>(_lc0.y, _lc1.y)); let _lc2 = df64_sub(df64_add(vec2<f32>(_v6.hi.x, _v6.lo.x), _cse8), _cse3); let _lc3 = df64_sub(df64_add(vec2<f32>(_v6.hi.y, _v6.lo.y), _cse8), _cse4); let _v7 = df64_sqrt(df64_add(df64_mul(_lc2, _lc2), df64_mul(_lc3, _lc3))); let _lc4 = df64_sub(df64_add(vec2<f32>(_v6.hi.x, _v6.lo.x), _cse8), _cse5); let _lc5 = df64_sub(df64_add(vec2<f32>(_v6.hi.y, _v6.lo.y), _cse8), _cse6); let _v8 = df64_sqrt(df64_add(df64_mul(_lc4, _lc4), df64_mul(_lc5, _lc5))); let _v9 = df64_narrow(df64_fract(df64_mul(df64_sub(df64_add(_v7, _cse8), df64_add(_v8, _cse8)), vec2<f32>(0.25, 0.0)))); let _v10 = df64_narrow(df64_fract(df64_mul(df64_add(_v7, _v8), vec2<f32>(0.0625, 0.0)))); let _v11 = vec2<f32>((df64_narrow(_cse1) + _v3), (df64_narrow(_cse2) + _v4)); let _v12 = length((_v11 - vec2<f32>(df64_narrow(_cse7), df64_narrow(_cse9)))); let _v13 = length((_v11 - vec2<f32>(df64_narrow(_cse10), df64_narrow(_cse11)))); let _v14 = fract(((_v12 - _v13) * 0.25)); let _v15 = fract(((_v12 + _v13) * 0.0625)); let _v16 = select(_v9, _v14, _v5); let _v17 = select(_v10, _v15, _v5); let _v18 = min(_v16, (1.0 - _v16)); let _v19 = min(_v17, (1.0 - _v17)); let _v20 = ((fwidth(_v18) * 1.2) + 0.0001); let _v21 = ((fwidth(_v19) * 1.2) + 0.0001); let _v22 = (1.0 - smoothstep(0.0, _v20, _v18)); let _v23 = (1.0 - smoothstep(0.0, _v21, _v19)); let _v24 = mix(vec3<f32>(0.02, 0.07, 0.13), vec3<f32>(0.04, 0.12, 0.2), vo.uv.y); return vec4<f32>((((_v24 + (vec3<f32>(0.0, 0.06, 0.08) * _v16)) + (vec3<f32>(0.25, 0.95, 0.95) * (_v22 * 0.9))) + (vec3<f32>(0.95, 0.7, 0.25) * (_v23 * 0.35))), 1.0);}
fn df64_twoSum(a: f32, b: f32) -> vec2<f32> { let _cse0 = textureLoad(_fp64, vec2<i32>(0, 0), 0).x; let _v0 = (a + b); let _v1 = (((_v0 * _cse0) - a) * _cse0); let _v2 = (((((a - ((_v0 - _v1) * _cse0)) * _cse0) * _cse0) * _cse0) + (b - _v1)); return vec2<f32>(_v0, _v2);}
fn df64_quickTwoSum(a: f32, b: f32) -> vec2<f32> { let _cse0 = textureLoad(_fp64, vec2<i32>(0, 0), 0).x; let _v0 = ((a + b) * _cse0); let _v1 = (b - ((_v0 - a) * _cse0)); return vec2<f32>(_v0, _v1);}
fn df64_split(a: f32) -> vec2<f32> { let _cse0 = textureLoad(_fp64, vec2<i32>(0, 0), 0).x; let _v0 = (a * (_cse0 * 4097.0)); let _v1 = ((_v0 * _cse0) - (_v0 - a)); let _v2 = ((a * _cse0) - _v1); return vec2<f32>(_v1, _v2);}
fn df64_twoProd(a: f32, b: f32) -> vec2<f32> { let _v0 = (a * b); let _v1 = df64_split(a); let _v2 = df64_split(b); let _v3 = (((((_v1.x * _v2.x) - _v0) + (_v1.x * _v2.y)) + (_v1.y * _v2.x)) + (_v1.y * _v2.y)); return vec2<f32>(_v0, _v3);}
fn df64_twoSqr(a: f32) -> vec2<f32> { let _cse0 = textureLoad(_fp64, vec2<i32>(0, 0), 0).x; let _v0 = (a * a); let _v1 = df64_split(a); let _v2 = (((((_v1.x * _v1.x) - _v0) * _cse0) + ((((_v1.x * _v1.y) * 2.0) * _cse0) * _cse0)) + ((((_v1.y * _v1.y) * _cse0) * _cse0) * _cse0)); return vec2<f32>(_v0, _v2);}
fn df64_add(a: vec2<f32>, b: vec2<f32>) -> vec2<f32> { var _v0: vec2<f32> = df64_twoSum(a.x, b.x); let _v1 = df64_twoSum(a.y, b.y); _v0.y = (_v0.y + _v1.x); _v0 = df64_quickTwoSum(_v0.x, _v0.y); _v0.y = (_v0.y + _v1.y); _v0 = df64_quickTwoSum(_v0.x, _v0.y); return _v0;}
fn df64_sub(a: vec2<f32>, b: vec2<f32>) -> vec2<f32> { return df64_add(a, (-b));}
fn df64_mul(a: vec2<f32>, b: vec2<f32>) -> vec2<f32> { var _v0: vec2<f32> = df64_twoProd(a.x, b.x); _v0.y = (_v0.y + (a.x * b.y)); _v0 = df64_quickTwoSum(_v0.x, _v0.y); _v0.y = (_v0.y + (a.y * b.x)); return df64_quickTwoSum(_v0.x, _v0.y);}
fn df64_sqrt(a: vec2<f32>) -> vec2<f32> { let _cse0 = textureLoad(_fp64, vec2<i32>(0, 0), 0).x; let _v0 = (_cse0 / sqrt(a.x)); let _v1 = (a.x * _v0); let _v2 = (df64_twoSqr(_v1) * _cse0); let _v3 = df64_sub(a, _v2).x; let _v4 = df64_twoProd((_v0 * 0.5), _v3); let _v5 = df64_add(vec2<f32>(_v1, 0.0), _v4); return select(_v5, vec2<f32>(0.0, 0.0), (a.x == 0.0));}
fn df64_floor(a: vec2<f32>) -> vec2<f32> { let _v0 = floor(a.x); return select(vec2<f32>(_v0, 0.0), df64_quickTwoSum(_v0, floor(a.y)), (_v0 == a.x));}
fn df64_fract(a: vec2<f32>) -> vec2<f32> { return df64_sub(a, df64_floor(a));}
fn df64_narrow(a: vec2<f32>) -> f32 { return (a.x + a.y);}#version 300 esprecision highp float;precision highp int;
struct VsOut { vec4 pos; vec2 uv;};out vec2 uv;
VsOut vs_impl(uint vi) { float _cse0 = ((float((vi & 1u)) * 4.0) - 1.0); float _cse1 = ((float((vi >> 1u)) * 4.0) - 1.0); return VsOut(vec4(_cse0, _cse1, 0.0, 1.0), vec2(((_cse0 * 0.5) + 0.5), ((_cse1 * 0.5) + 0.5)));}
void main() { VsOut _out = vs_impl(uint(gl_VertexID)); gl_Position = _out.pos; uv = _out.uv;}#version 300 esprecision highp float;precision highp int;
struct VsOut { vec4 pos; vec2 uv;};
struct DF64Vec2 { vec2 hi; vec2 lo;};layout(std140) uniform Uniforms { DF64Vec2 center; DF64Vec2 st_a; DF64Vec2 st_b; vec2 resolution; float zoom_exp; float fp64;} u;
uniform sampler2D _fp64;vec2 df64_twoSum(float a, float b);vec2 df64_quickTwoSum(float a, float b);vec2 df64_split(float a);vec2 df64_twoProd(float a, float b);vec2 df64_twoSqr(float a);vec2 df64_add(vec2 a, vec2 b);vec2 df64_sub(vec2 a, vec2 b);vec2 df64_mul(vec2 a, vec2 b);vec2 df64_sqrt(vec2 a);vec2 df64_floor(vec2 a);vec2 df64_fract(vec2 a);float df64_narrow(vec2 a);vec2 df64_twoSum(float a, float b) { float _cse0 = texelFetch(_fp64, ivec2(0, 0), 0).x; float _v0 = (a + b); float _v1 = (((_v0 * _cse0) - a) * _cse0); float _v2 = (((((a - ((_v0 - _v1) * _cse0)) * _cse0) * _cse0) * _cse0) + (b - _v1)); return vec2(_v0, _v2);}
vec2 df64_quickTwoSum(float a, float b) { float _cse0 = texelFetch(_fp64, ivec2(0, 0), 0).x; float _v0 = ((a + b) * _cse0); float _v1 = (b - ((_v0 - a) * _cse0)); return vec2(_v0, _v1);}
vec2 df64_split(float a) { float _cse0 = texelFetch(_fp64, ivec2(0, 0), 0).x; float _v0 = (a * (_cse0 * 4097.0)); float _v1 = ((_v0 * _cse0) - (_v0 - a)); float _v2 = ((a * _cse0) - _v1); return vec2(_v1, _v2);}
vec2 df64_twoProd(float a, float b) { float _v0 = (a * b); vec2 _v1 = df64_split(a); vec2 _v2 = df64_split(b); float _v3 = (((((_v1.x * _v2.x) - _v0) + (_v1.x * _v2.y)) + (_v1.y * _v2.x)) + (_v1.y * _v2.y)); return vec2(_v0, _v3);}
vec2 df64_twoSqr(float a) { float _cse0 = texelFetch(_fp64, ivec2(0, 0), 0).x; float _v0 = (a * a); vec2 _v1 = df64_split(a); float _v2 = (((((_v1.x * _v1.x) - _v0) * _cse0) + ((((_v1.x * _v1.y) * 2.0) * _cse0) * _cse0)) + ((((_v1.y * _v1.y) * _cse0) * _cse0) * _cse0)); return vec2(_v0, _v2);}
vec2 df64_add(vec2 a, vec2 b) { vec2 _v0 = df64_twoSum(a.x, b.x); vec2 _v1 = df64_twoSum(a.y, b.y); _v0.y = (_v0.y + _v1.x); _v0 = df64_quickTwoSum(_v0.x, _v0.y); _v0.y = (_v0.y + _v1.y); _v0 = df64_quickTwoSum(_v0.x, _v0.y); return _v0;}
vec2 df64_sub(vec2 a, vec2 b) { return df64_add(a, (-b));}
vec2 df64_mul(vec2 a, vec2 b) { vec2 _v0 = df64_twoProd(a.x, b.x); _v0.y = (_v0.y + (a.x * b.y)); _v0 = df64_quickTwoSum(_v0.x, _v0.y); _v0.y = (_v0.y + (a.y * b.x)); return df64_quickTwoSum(_v0.x, _v0.y);}
vec2 df64_sqrt(vec2 a) { float _cse0 = texelFetch(_fp64, ivec2(0, 0), 0).x; float _v0 = (_cse0 / sqrt(a.x)); float _v1 = (a.x * _v0); vec2 _v2 = (df64_twoSqr(_v1) * _cse0); float _v3 = df64_sub(a, _v2).x; vec2 _v4 = df64_twoProd((_v0 * 0.5), _v3); vec2 _v5 = df64_add(vec2(_v1, 0.0), _v4); return ((a.x == 0.0) ? vec2(0.0, 0.0) : _v5);}
vec2 df64_floor(vec2 a) { float _v0 = floor(a.x); return ((_v0 == a.x) ? df64_quickTwoSum(_v0, floor(a.y)) : vec2(_v0, 0.0));}
vec2 df64_fract(vec2 a) { return df64_sub(a, df64_floor(a));}
float df64_narrow(vec2 a) { return (a.x + a.y);}in vec2 uv;layout(location = 0) out vec4 _ret;
vec4 fs_loran_impl(VsOut vo) { vec2 _cse7 = vec2(u.st_a.hi.x, u.st_a.lo.x); vec2 _cse8 = vec2(0.0, 0.0); vec2 _cse9 = vec2(u.st_a.hi.y, u.st_a.lo.y); vec2 _cse10 = vec2(u.st_b.hi.x, u.st_b.lo.x); vec2 _cse11 = vec2(u.st_b.hi.y, u.st_b.lo.y); bool _cse0 = (vo.uv.x < 0.5); vec2 _cse1 = vec2(u.center.hi.x, u.center.lo.x); vec2 _cse2 = vec2(u.center.hi.y, u.center.lo.y); vec2 _cse3 = df64_add(_cse7, _cse8); vec2 _cse4 = df64_add(_cse9, _cse8); vec2 _cse5 = df64_add(_cse10, _cse8); vec2 _cse6 = df64_add(_cse11, _cse8); float _v0 = pow(10.0, (-u.zoom_exp)); float _v1 = (vo.uv.x * 2.0); float _v2 = (_v1 - (_cse0 ? 0.0 : 1.0)); float _v3 = ((_v2 - 0.5) * _v0); float _v4 = (((vo.uv.y - 0.5) * _v0) * ((u.resolution.y / u.resolution.x) * 2.0)); bool _v5 = (_cse0 || (u.fp64 < 0.5)); vec2 _lc0 = df64_add(_cse1, vec2(_v3, 0.0)); vec2 _lc1 = df64_add(_cse2, vec2(_v4, 0.0)); DF64Vec2 _v6 = DF64Vec2(vec2(_lc0.x, _lc1.x), vec2(_lc0.y, _lc1.y)); vec2 _lc2 = df64_sub(df64_add(vec2(_v6.hi.x, _v6.lo.x), _cse8), _cse3); vec2 _lc3 = df64_sub(df64_add(vec2(_v6.hi.y, _v6.lo.y), _cse8), _cse4); vec2 _v7 = df64_sqrt(df64_add(df64_mul(_lc2, _lc2), df64_mul(_lc3, _lc3))); vec2 _lc4 = df64_sub(df64_add(vec2(_v6.hi.x, _v6.lo.x), _cse8), _cse5); vec2 _lc5 = df64_sub(df64_add(vec2(_v6.hi.y, _v6.lo.y), _cse8), _cse6); vec2 _v8 = df64_sqrt(df64_add(df64_mul(_lc4, _lc4), df64_mul(_lc5, _lc5))); float _v9 = df64_narrow(df64_fract(df64_mul(df64_sub(df64_add(_v7, _cse8), df64_add(_v8, _cse8)), vec2(0.25, 0.0)))); float _v10 = df64_narrow(df64_fract(df64_mul(df64_add(_v7, _v8), vec2(0.0625, 0.0)))); vec2 _v11 = vec2((df64_narrow(_cse1) + _v3), (df64_narrow(_cse2) + _v4)); float _v12 = length((_v11 - vec2(df64_narrow(_cse7), df64_narrow(_cse9)))); float _v13 = length((_v11 - vec2(df64_narrow(_cse10), df64_narrow(_cse11)))); float _v14 = fract(((_v12 - _v13) * 0.25)); float _v15 = fract(((_v12 + _v13) * 0.0625)); float _v16 = (_v5 ? _v14 : _v9); float _v17 = (_v5 ? _v15 : _v10); float _v18 = min(_v16, (1.0 - _v16)); float _v19 = min(_v17, (1.0 - _v17)); float _v20 = ((fwidth(_v18) * 1.2) + 0.0001); float _v21 = ((fwidth(_v19) * 1.2) + 0.0001); float _v22 = (1.0 - smoothstep(0.0, _v20, _v18)); float _v23 = (1.0 - smoothstep(0.0, _v21, _v19)); vec3 _v24 = mix(vec3(0.02, 0.07, 0.13), vec3(0.04, 0.12, 0.2), vo.uv.y); return vec4((((_v24 + (vec3(0.0, 0.06, 0.08) * _v16)) + (vec3(0.25, 0.95, 0.95) * (_v22 * 0.9))) + (vec3(0.95, 0.7, 0.25) * (_v23 * 0.35))), 1.0);}
void main() { VsOut vo; vo.pos = gl_FragCoord; vo.uv = uv; _ret = fs_loran_impl(vo);}{ "bindGroups": [ { "group": 0, "entries": [ { "group": 0, "binding": 0, "name": "u", "space": "uniform", "resourceKind": "uniform-buffer", "structName": "Uniforms" } ] } ], "uniforms": [ { "name": "Uniforms", "size": 64, "align": 16, "fields": [ { "name": "center", "type": "vec2<f64>", "offset": 0, "align": 16, "size": 16 }, { "name": "st_a", "type": "vec2<f64>", "offset": 16, "align": 16, "size": 16 }, { "name": "st_b", "type": "vec2<f64>", "offset": 32, "align": 16, "size": 16 }, { "name": "resolution", "type": "vec2<f32>", "offset": 48, "align": 8, "size": 8 }, { "name": "zoom_exp", "type": "f32", "offset": 56, "align": 4, "size": 4 }, { "name": "fp64", "type": "f32", "offset": 60, "align": 4, "size": 4 } ] } ], "storage": [], "entries": [ { "name": "vs", "stage": "vertex", "inputs": [ "u32" ], "output": "struct:VsOut" }, { "name": "fs_loran", "stage": "fragment", "inputs": [ "struct:VsOut" ], "output": "vec4<f32>" } ], "overrides": []}
Run it locally:
npx tsx examples/print.ts fp64-loran
— prints the WGSL, GLSL, reflection. Source:
GitHub.
New to the IR? See Concepts; the full authoring + emit surface is in the API reference.