fp64 hyperbolic navigation

fp64 hyperbolic navigation

A LORAN-style chart grid.

// ═══ typeshade 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.js';
import { VsOut, vs } from './_fullscreen.js';
import type { ShaderExample } from './_shared.js';
// 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 {
@align(16) center: DF64Vec2,
@align(16) st_a: DF64Vec2,
@align(16) 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>;
@vertex
fn 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)));
}
@fragment
fn fs_loran(vo: VsOut) -> @location(0) vec4<f32> {
let _fp64_g = textureLoad(_fp64, vec2<i32>(0, 0), 0).x;
let _v0 = pow(10.0, (-u.zoom_exp));
let _v1 = (vo.uv.x * 2.0);
let _cse0 = (vo.uv.x < 0.5);
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 _cse1 = vec2<f32>(u.center.hi.x, u.center.lo.x);
let _cse2 = vec2<f32>(u.center.hi.y, u.center.lo.y);
let _lc0 = df64_add(_cse1, vec2<f32>(_v3, 0.0), _fp64_g);
let _lc1 = df64_add(_cse2, vec2<f32>(_v4, 0.0), _fp64_g);
let _v6 = DF64Vec2(vec2<f32>(_lc0.x, _lc1.x), vec2<f32>(_lc0.y, _lc1.y));
let _cse8 = bitcast<f32>(bitcast<u32>(0.0));
let _cse3 = vec2<f32>(_cse8, _cse8);
let _cse4 = vec2<f32>(u.st_a.hi.x, u.st_a.lo.x);
let _cse5 = vec2<f32>(u.st_a.hi.y, u.st_a.lo.y);
let _gv0 = df64_add(vec2<f32>(_v6.hi.x, _v6.lo.x), _cse3, _fp64_g);
let _gv1 = df64_add(vec2<f32>(_v6.hi.y, _v6.lo.y), _cse3, _fp64_g);
let _v7 = df64_sqrt(df64_add(df64_sqr(df64_sub(_gv0, df64_add(_cse4, _cse3, _fp64_g), _fp64_g), _fp64_g), df64_sqr(df64_sub(_gv1, df64_add(_cse5, _cse3, _fp64_g), _fp64_g), _fp64_g), _fp64_g), _fp64_g);
let _cse6 = vec2<f32>(u.st_b.hi.x, u.st_b.lo.x);
let _cse7 = vec2<f32>(u.st_b.hi.y, u.st_b.lo.y);
let _v8 = df64_sqrt(df64_add(df64_sqr(df64_sub(_gv0, df64_add(_cse6, _cse3, _fp64_g), _fp64_g), _fp64_g), df64_sqr(df64_sub(_gv1, df64_add(_cse7, _cse3, _fp64_g), _fp64_g), _fp64_g), _fp64_g), _fp64_g);
let _v9 = df64_narrow(df64_fract((df64_sub(df64_add(_v7, _cse3, _fp64_g), df64_add(_v8, _cse3, _fp64_g), _fp64_g) * 0.25), _fp64_g));
let _v10 = df64_narrow(df64_fract((df64_add(_v7, _v8, _fp64_g) * 0.0625), _fp64_g));
let _v11 = vec2<f32>((df64_narrow(_cse1) + _v3), (df64_narrow(_cse2) + _v4));
let _v12 = length((_v11 - vec2<f32>(df64_narrow(_cse4), df64_narrow(_cse5))));
let _v13 = length((_v11 - vec2<f32>(df64_narrow(_cse6), df64_narrow(_cse7))));
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, _fp64_g: f32) -> vec2<f32> {
let _v0 = (a + b);
let _v1 = (((_v0 * _fp64_g) - a) * _fp64_g);
let _v2 = (((a - ((_v0 - _v1) * _fp64_g)) * _fp64_g) + (b - _v1));
return vec2<f32>(_v0, _v2);
}
fn df64_quickTwoSum(a: f32, b: f32, _fp64_g: f32) -> vec2<f32> {
let _v0 = ((a + b) * _fp64_g);
let _v1 = (b - ((_v0 - a) * _fp64_g));
return vec2<f32>(_v0, _v1);
}
fn df64_split(a: f32, _fp64_g: f32) -> vec2<f32> {
let _v0 = (a * (_fp64_g * 4097.0));
let _v1 = ((_v0 * _fp64_g) - (_v0 - a));
let _v2 = ((a * _fp64_g) - _v1);
return vec2<f32>(_v1, _v2);
}
fn df64_twoProd(a: f32, b: f32, _fp64_g: f32) -> vec2<f32> {
let _v0 = (a * b);
let _v1 = df64_split(a, _fp64_g);
let _v2 = df64_split(b, _fp64_g);
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, _fp64_g: f32) -> vec2<f32> {
let _v0 = (a * a);
let _v1 = df64_split(a, _fp64_g);
let _v2 = (((((_v1.x * _v1.x) - _v0) * _fp64_g) + (((_v1.x * _v1.y) * 2.0) * _fp64_g)) + ((_v1.y * _v1.y) * _fp64_g));
return vec2<f32>(_v0, _v2);
}
fn df64_add(a: vec2<f32>, b: vec2<f32>, _fp64_g: f32) -> vec2<f32> {
var _v0: vec2<f32> = df64_twoSum(a.x, b.x, _fp64_g);
let _v1 = df64_twoSum(a.y, b.y, _fp64_g);
_v0.y = (_v0.y + _v1.x);
_v0 = df64_quickTwoSum(_v0.x, _v0.y, _fp64_g);
_v0.y = (_v0.y + _v1.y);
_v0 = df64_quickTwoSum(_v0.x, _v0.y, _fp64_g);
return _v0;
}
fn df64_sub(a: vec2<f32>, b: vec2<f32>, _fp64_g: f32) -> vec2<f32> {
return df64_add(a, (-b), _fp64_g);
}
fn df64_sqr(a: vec2<f32>, _fp64_g: f32) -> vec2<f32> {
var _v0: vec2<f32> = df64_twoSqr(a.x, _fp64_g);
_v0.y = (_v0.y + ((a.x * a.y) * 2.0));
return df64_quickTwoSum(_v0.x, _v0.y, _fp64_g);
}
fn df64_sqrt(a: vec2<f32>, _fp64_g: f32) -> vec2<f32> {
let _v0 = (_fp64_g / sqrt(a.x));
let _v1 = (a.x * _v0);
let _v2 = (df64_twoSqr(_v1, _fp64_g) * _fp64_g);
let _v3 = df64_sub(a, _v2, _fp64_g).x;
let _v4 = df64_twoProd((_v0 * 0.5), _v3, _fp64_g);
let _v5 = df64_add(vec2<f32>(_v1, 0.0), _v4, _fp64_g);
return select(_v5, vec2<f32>(0.0, 0.0), (a.x == 0.0));
}
fn df64_floor(a: vec2<f32>, _fp64_g: f32) -> vec2<f32> {
let _v0 = floor(a.x);
return select(vec2<f32>(_v0, 0.0), df64_quickTwoSum(_v0, floor(a.y), _fp64_g), (_v0 == a.x));
}
fn df64_fract(a: vec2<f32>, _fp64_g: f32) -> vec2<f32> {
return df64_sub(a, df64_floor(a, _fp64_g), _fp64_g);
}
fn df64_narrow(a: vec2<f32>) -> f32 {
return (a.x + a.y);
}
#version 300 es
precision highp float;
precision highp int;
out vec2 uv;
void main() {
uint vi = uint(gl_VertexID);
float _cse0 = ((float((vi & 1u)) * 4.0) - 1.0);
float _cse1 = ((float((vi >> 1u)) * 4.0) - 1.0);
gl_Position = vec4(_cse0, _cse1, 0.0, 1.0);
uv = vec2(((_cse0 * 0.5) + 0.5), ((_cse1 * 0.5) + 0.5));
}
#version 300 es
precision highp float;
precision highp int;
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 highp sampler2D _fp64;
vec2 df64_twoSum(float a, float b, float _fp64_g) {
float _v0 = (a + b);
float _v1 = (((_v0 * _fp64_g) - a) * _fp64_g);
float _v2 = (((a - ((_v0 - _v1) * _fp64_g)) * _fp64_g) + (b - _v1));
return vec2(_v0, _v2);
}
vec2 df64_quickTwoSum(float a, float b, float _fp64_g) {
float _v0 = ((a + b) * _fp64_g);
float _v1 = (b - ((_v0 - a) * _fp64_g));
return vec2(_v0, _v1);
}
vec2 df64_split(float a, float _fp64_g) {
float _v0 = (a * (_fp64_g * 4097.0));
float _v1 = ((_v0 * _fp64_g) - (_v0 - a));
float _v2 = ((a * _fp64_g) - _v1);
return vec2(_v1, _v2);
}
vec2 df64_twoProd(float a, float b, float _fp64_g) {
float _v0 = (a * b);
vec2 _v1 = df64_split(a, _fp64_g);
vec2 _v2 = df64_split(b, _fp64_g);
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 _fp64_g) {
float _v0 = (a * a);
vec2 _v1 = df64_split(a, _fp64_g);
float _v2 = (((((_v1.x * _v1.x) - _v0) * _fp64_g) + (((_v1.x * _v1.y) * 2.0) * _fp64_g)) + ((_v1.y * _v1.y) * _fp64_g));
return vec2(_v0, _v2);
}
vec2 df64_add(vec2 a, vec2 b, float _fp64_g) {
vec2 _v0 = df64_twoSum(a.x, b.x, _fp64_g);
vec2 _v1 = df64_twoSum(a.y, b.y, _fp64_g);
_v0.y = (_v0.y + _v1.x);
_v0 = df64_quickTwoSum(_v0.x, _v0.y, _fp64_g);
_v0.y = (_v0.y + _v1.y);
_v0 = df64_quickTwoSum(_v0.x, _v0.y, _fp64_g);
return _v0;
}
vec2 df64_sub(vec2 a, vec2 b, float _fp64_g) {
return df64_add(a, (-b), _fp64_g);
}
vec2 df64_sqr(vec2 a, float _fp64_g) {
vec2 _v0 = df64_twoSqr(a.x, _fp64_g);
_v0.y = (_v0.y + ((a.x * a.y) * 2.0));
return df64_quickTwoSum(_v0.x, _v0.y, _fp64_g);
}
vec2 df64_sqrt(vec2 a, float _fp64_g) {
float _v0 = (_fp64_g / sqrt(a.x));
float _v1 = (a.x * _v0);
vec2 _v2 = (df64_twoSqr(_v1, _fp64_g) * _fp64_g);
float _v3 = df64_sub(a, _v2, _fp64_g).x;
vec2 _v4 = df64_twoProd((_v0 * 0.5), _v3, _fp64_g);
vec2 _v5 = df64_add(vec2(_v1, 0.0), _v4, _fp64_g);
return ((a.x == 0.0) ? vec2(0.0, 0.0) : _v5);
}
vec2 df64_floor(vec2 a, float _fp64_g) {
float _v0 = floor(a.x);
return ((_v0 == a.x) ? df64_quickTwoSum(_v0, floor(a.y), _fp64_g) : vec2(_v0, 0.0));
}
vec2 df64_fract(vec2 a, float _fp64_g) {
return df64_sub(a, df64_floor(a, _fp64_g), _fp64_g);
}
float df64_narrow(vec2 a) {
return (a.x + a.y);
}
in vec2 uv;
layout(location = 0) out vec4 _ret;
void main() {
float _fp64_g = texelFetch(_fp64, ivec2(0, 0), 0).x;
float _v0 = pow(10.0, (-u.zoom_exp));
float _v1 = (uv.x * 2.0);
bool _cse0 = (uv.x < 0.5);
float _v2 = (_v1 - (_cse0 ? 0.0 : 1.0));
float _v3 = ((_v2 - 0.5) * _v0);
float _v4 = (((uv.y - 0.5) * _v0) * ((u.resolution.y / u.resolution.x) * 2.0));
bool _v5 = (_cse0 || (u.fp64 < 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 _lc0 = df64_add(_cse1, vec2(_v3, 0.0), _fp64_g);
vec2 _lc1 = df64_add(_cse2, vec2(_v4, 0.0), _fp64_g);
DF64Vec2 _v6 = DF64Vec2(vec2(_lc0.x, _lc1.x), vec2(_lc0.y, _lc1.y));
float _cse8 = uintBitsToFloat(floatBitsToUint(0.0));
vec2 _cse3 = vec2(_cse8, _cse8);
vec2 _cse4 = vec2(u.st_a.hi.x, u.st_a.lo.x);
vec2 _cse5 = vec2(u.st_a.hi.y, u.st_a.lo.y);
vec2 _gv0 = df64_add(vec2(_v6.hi.x, _v6.lo.x), _cse3, _fp64_g);
vec2 _gv1 = df64_add(vec2(_v6.hi.y, _v6.lo.y), _cse3, _fp64_g);
vec2 _v7 = df64_sqrt(df64_add(df64_sqr(df64_sub(_gv0, df64_add(_cse4, _cse3, _fp64_g), _fp64_g), _fp64_g), df64_sqr(df64_sub(_gv1, df64_add(_cse5, _cse3, _fp64_g), _fp64_g), _fp64_g), _fp64_g), _fp64_g);
vec2 _cse6 = vec2(u.st_b.hi.x, u.st_b.lo.x);
vec2 _cse7 = vec2(u.st_b.hi.y, u.st_b.lo.y);
vec2 _v8 = df64_sqrt(df64_add(df64_sqr(df64_sub(_gv0, df64_add(_cse6, _cse3, _fp64_g), _fp64_g), _fp64_g), df64_sqr(df64_sub(_gv1, df64_add(_cse7, _cse3, _fp64_g), _fp64_g), _fp64_g), _fp64_g), _fp64_g);
float _v9 = df64_narrow(df64_fract((df64_sub(df64_add(_v7, _cse3, _fp64_g), df64_add(_v8, _cse3, _fp64_g), _fp64_g) * 0.25), _fp64_g));
float _v10 = df64_narrow(df64_fract((df64_add(_v7, _v8, _fp64_g) * 0.0625), _fp64_g));
vec2 _v11 = vec2((df64_narrow(_cse1) + _v3), (df64_narrow(_cse2) + _v4));
float _v12 = length((_v11 - vec2(df64_narrow(_cse4), df64_narrow(_cse5))));
float _v13 = length((_v11 - vec2(df64_narrow(_cse6), df64_narrow(_cse7))));
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), uv.y);
_ret = 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);
}

This example is steered by a control the page has no value for, so the page shows no picture.

The WGSL and GLSL tabs are the compiler's own output at commit 26de7be8, read from the goldens its emit suite bakes (emit-goldens.test.ts).

This example is written against the fn() builder API, which the editor in the Playground does not take.

File on GitHub

Edit this page Report a problem