ONEIRIC 1 - RETROSCOPIC CRT SHADER SOURCE CODE
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Oneiric Sculpture 1</title>
<style>
html, body { margin:0; padding:0; overflow:hidden; background:#000; height:100%; width:100%; cursor: crosshair; font-family: -apple-system, "Segoe UI", sans-serif; }
canvas {
display:block;
width:100vw;
height:100vh;
filter: grayscale(1) sepia(1) hue-rotate(80deg) saturate(5) brightness(1.15) contrast(1.2);
position: relative;
z-index: 2;
}
#hint {
position:fixed; left:50%; bottom:28px; transform:translateX(-50%);
color:rgba(220,230,255,0.55); font-family: Georgia, 'Times New Roman', serif;
font-size:13px; letter-spacing:0.12em; text-transform:uppercase;
pointer-events:none; transition:opacity 2.5s ease; opacity:1; text-align:center;
text-shadow: 0 0 12px rgba(120,160,255,0.6);
z-index: 5;
}
#hint.fade { opacity:0; }
/* Retroscopic Overlay */
#scope {
position: fixed;
inset: 0;
z-index: 4;
pointer-events: none;
display: block;
}
#scope>div {
position: absolute;
inset: 0;
}
#scope .glow {
background: radial-gradient(circle at 50% 46%, rgba(30, 255, 130, .08), rgba(0, 0, 0, 0) 62%);
animation: nvflick 3.6s steps(24) infinite;
}
#scope .scan {
background: repeating-linear-gradient(to bottom, rgba(0, 0, 0, 0) 0 2px, rgba(0, 25, 8, .45) 2px 3px);
animation: nvscan 7s linear infinite;
}
#scope .vig {
box-shadow: inset 0 0 200px 50px rgba(0, 0, 0, .8);
}
#scope .mask {
background: radial-gradient(circle at 50% 50%, rgba(0, 0, 0, 0) 54%, rgba(0, 0, 0, .5) 72%, rgba(0, 0, 0, .94) 100%);
}
@keyframes nvscan {
from { background-position: 0 0; }
to { background-position: 0 240px; }
}
@keyframes nvflick {
0%, 100% { opacity: .55; }
40% { opacity: .9; }
62% { opacity: .4; }
80% { opacity: .75; }
}
</style>
</head>
<body>
<canvas id="c"></canvas>
<div id="scope">
<div class="glow"></div>
<div class="scan"></div>
<div class="vig"></div>
<div class="mask"></div>
</div>
<div id="hint">Click to interact</div>
<script>
const canvas = document.getElementById('c');
const gl = canvas.getContext('webgl2', { antialias:false, depth:false, stencil:false, powerPreference:'high-performance' });
if (!gl) { document.body.innerHTML = '<div style="color:#fff;font-family:sans-serif;padding:40px;">WebGL2 is required for this piece.</div>'; }
function resize(){
const scale = 0.6; // Downscale to improve performance
const w = Math.floor(window.innerWidth * scale);
const h = Math.floor(window.innerHeight * scale);
if (canvas.width !== w || canvas.height !== h) {
canvas.width = w; canvas.height = h;
}
}
window.addEventListener('resize', resize);
resize();
const vsSrc = `#version 300 es
precision highp float;
const vec2 verts[3] = vec2[3](vec2(-1.0,-1.0), vec2(3.0,-1.0), vec2(-1.0,3.0));
out vec2 vUv;
void main(){
vec2 p = verts[gl_VertexID];
vUv = p;
gl_Position = vec4(p, 0.0, 1.0);
}`;
const fsSrc = `#version 300 es
precision highp float;
in vec2 vUv;
out vec4 fragColor;
uniform vec2 uRes;
uniform float uTime;
uniform vec2 uMouse; // normalized -1..1, smoothed
uniform vec2 uMouseVel; // velocity of mouse, smoothed
uniform float uBloomState; // 0..1 evolving "wakefulness" of the lattice
uniform float uClickEnergy[8];
uniform vec2 uClickPos[8];
uniform float uClickTime[8];
#define PI 3.14159265359
#define TAU 6.28318530718
// ---------- hashing / noise ----------
float hash11(float p){ p = fract(p*0.1031); p*=p+33.33; p*=p+p; return fract(p); }
float hash21(vec2 p){
vec3 p3 = fract(vec3(p.xyx) * 0.1031);
p3 += dot(p3, p3.yzx + 33.33);
return fract((p3.x + p3.y) * p3.z);
}
vec3 hash33(vec3 p){
p = vec3(dot(p,vec3(127.1,311.7,74.7)),
dot(p,vec3(269.5,183.3,246.1)),
dot(p,vec3(113.5,271.9,124.6)));
return fract(sin(p)*43758.5453123);
}
float vnoise(vec3 p){
vec3 i = floor(p); vec3 f = fract(p);
vec3 u = f*f*(3.0-2.0*f);
float n00 = hash21(i.xy + i.z*57.0);
float n10 = hash21(i.xy + vec2(1,0) + i.z*57.0);
float n01 = hash21(i.xy + vec2(0,1) + i.z*57.0);
float n11 = hash21(i.xy + vec2(1,1) + i.z*57.0);
float nz0 = mix(mix(n00,n10,u.x), mix(n01,n11,u.x), u.y);
float n00b = hash21(i.xy + (i.z+1.0)*57.0);
float n10b = hash21(i.xy + vec2(1,0) + (i.z+1.0)*57.0);
float n01b = hash21(i.xy + vec2(0,1) + (i.z+1.0)*57.0);
float n11b = hash21(i.xy + vec2(1,1) + (i.z+1.0)*57.0);
float nz1 = mix(mix(n00b,n10b,u.x), mix(n01b,n11b,u.x), u.y);
return mix(nz0, nz1, u.z);
}
float fbm(vec3 p, int oct){
float a = 0.5, s = 0.0;
for(int i=0;i<5;i++){
if(i>=oct) break;
s += a*vnoise(p);
p = p*2.02 + vec3(7.1,3.3,5.7);
a *= 0.55;
}
return s;
}
// ---------- rotation ----------
mat2 rot2(float a){ float s=sin(a), c=cos(a); return mat2(c,-s,s,c); }
mat3 rotAxis(vec3 axis, float angle){
axis = normalize(axis);
float s = sin(angle), c = cos(angle), oc = 1.0-c;
return mat3(
oc*axis.x*axis.x+c, oc*axis.x*axis.y-axis.z*s, oc*axis.z*axis.x+axis.y*s,
oc*axis.x*axis.y+axis.z*s, oc*axis.y*axis.y+c, oc*axis.y*axis.z-axis.x*s,
oc*axis.z*axis.x-axis.y*s, oc*axis.y*axis.z+axis.x*s, oc*axis.z*axis.z+c
);
}
// ---------- SDF primitives ----------
float sdSphere(vec3 p, float r){ return length(p)-r; }
float sdBox(vec3 p, vec3 b){
vec3 q = abs(p)-b;
return length(max(q,0.0)) + min(max(q.x,max(q.y,q.z)),0.0);
}
float sdTorus(vec3 p, vec2 t){
vec2 q = vec2(length(p.xz)-t.x, p.y);
return length(q)-t.y;
}
float sdOctahedron(vec3 p, float s){
p = abs(p);
float m = p.x+p.y+p.z-s;
vec3 q;
if (3.0*p.x < m) q = p.xyz;
else if (3.0*p.y < m) q = p.yzx;
else if (3.0*p.z < m) q = p.zxy;
else return m*0.57735027;
float k = clamp(0.5*(q.z-q.y+s),0.0,s);
return length(vec3(q.x,q.y-s+k,q.z-k));
}
float smin(float a, float b, float k){
float h = clamp(0.5+0.5*(b-a)/k, 0.0, 1.0);
return mix(b,a,h) - k*h*(1.0-h);
}
float smax(float a, float b, float k){
return -smin(-a,-b,k);
}
// fold helpers for self-similarity
void foldAbs(inout vec3 p, vec3 n){ p -= 2.0*min(dot(p,n),0.0)*n; }
// global click-driven displacement field (warps space, "growth", "ruptures")
vec3 clickField(vec3 p, float t){
vec3 disp = vec3(0.0);
for(int i=0;i<8;i++){
float e = uClickEnergy[i];
if (e <= 0.001) continue;
float age = t - uClickTime[i];
vec3 cpos = vec3(uClickPos[i]*4.5, 0.0);
float d = length(p - cpos);
float wave = sin(d*3.0 - age*5.0) * exp(-age*0.6) * exp(-d*0.35);
disp += normalize(p - cpos + 0.0001) * wave * e * 0.35;
}
return disp;
}
float clickGlowField(vec3 p, float t){
float g = 0.0;
for(int i=0;i<8;i++){
float e = uClickEnergy[i];
if (e <= 0.001) continue;
float age = t - uClickTime[i];
vec3 cpos = vec3(uClickPos[i]*4.5, 0.0);
float d = length(p - cpos);
float ring = exp(-pow((d - age*2.2)*2.0, 2.0)) * exp(-age*0.4);
g += ring * e;
}
return g;
}
// the evolving lattice: a recursive, folded, mouse-perturbed SDF
// returns distance and writes a material id channel via global
float matId;
float latticeColorPhase;
float deLattice(vec3 p, float t){
// mouse perturbs the underlying field - "spatial distortion"
vec3 mInfluence = vec3(uMouse*2.6, 0.6*sin(t*0.13));
float distToMouse = length(p - mInfluence);
vec3 warp = (p - mInfluence) * (0.16 * exp(-distToMouse*0.7)) * (1.0 + length(uMouseVel)*6.0);
p -= warp;
p += clickField(p, t);
// slow global breathing / morphogenesis driven by uBloomState
float wake = uBloomState;
// domain repetition with non-euclidean-ish scale drift across radius
float r = length(p);
float ang = atan(p.z, p.x);
float bend = sin(r*0.35 + t*0.05)*0.35*wake;
p.xz = rot2(bend) * p.xz;
vec3 q = p;
float scale = 1.0;
float d = 1e5;
float idAccum = 0.0;
// recursive folded kaleidoscopic chamber structure
for(int i=0;i<4;i++){
float fi = float(i);
q = abs(q) - (0.85 + 0.15*sin(t*0.07+fi));
q.xy *= rot2(0.35 + 0.12*sin(t*0.11+fi*1.7) + 0.25*wake);
q.yz *= rot2(0.21*cos(t*0.05+fi*2.3));
float s = 1.55 + 0.25*sin(fi*1.3 + t*0.04);
q *= s; scale *= s;
float local = sdOctahedron(q, 0.92 + 0.18*sin(t*0.2+fi));
local = smin(local, sdTorus(q.xzy, vec2(0.65,0.18+0.06*sin(t*0.3+fi))), 0.45);
local /= scale;
if (local < d){ d = local; idAccum = fi; }
}
// a slower "geological/architectural" base mass binding everything
vec3 bp = p;
float base = sdBox(bp, vec3(2.6,1.3,2.6));
base = smax(base, -sdBox(bp, vec3(2.2,2.6,1.0)), 0.5);
base = smax(base, -sdBox(bp, vec3(1.0,2.6,2.2)), 0.5);
float noiseDetail = fbm(p*1.7 + t*0.02, 2) * 0.18 * (0.4+0.6*wake);
base += noiseDetail;
float result = smin(d, base, 1.1);
// Microscopic ornamentation removed for performance in retro filter
// float fine = fbm(p*9.0 + t*0.1, 2) * 0.022;
// result += fine;
// floating satellite fragments — biological/alien growths that bloom with wakefulness
for(int k=0;k<2;k++){
float fk = float(k);
vec3 orbitP = p;
float orbAngle = t*0.18 + fk*TAU/3.0;
vec3 center = vec3(cos(orbAngle)*3.2, sin(orbAngle*1.3)*1.6 + sin(t*0.1+fk)*0.6, sin(orbAngle)*3.2);
vec3 op = orbitP - center;
op = rotAxis(vec3(0.3,1.0,0.2), t*0.3+fk) * op;
float satellite = sdOctahedron(op, (0.25 + 0.18*wake) * (0.7+0.3*sin(t*0.6+fk*2.0)));
satellite = smin(satellite, sdTorus(op.xzy, vec2(0.18,0.05)), 0.12);
result = smin(result, satellite, 0.35);
}
matId = idAccum;
latticeColorPhase = r*0.4 + idAccum*1.3;
return result * 0.92;
}
float deScene(vec3 p, float t){
return deLattice(p, t);
}
vec3 calcNormal(vec3 p, float t){
float eps = 0.0015;
vec2 e = vec2(1.0,-1.0)*eps;
return normalize(
e.xyy*deScene(p+e.xyy,t) + e.yyx*deScene(p+e.yyx,t) +
e.yxy*deScene(p+e.yxy,t) + e.xxx*deScene(p+e.xxx,t)
);
}
// soft shadow via raymarch
float softShadow(vec3 ro, vec3 rd, float t){
float res = 1.0;
float dist = 0.05;
for(int i=0;i<8;i++){
float h = deScene(ro + rd*dist, t);
res = min(res, h*10.0/dist);
dist += clamp(h, 0.03, 0.6);
if (h < 0.002 || dist > 6.0) break;
}
return clamp(res, 0.0, 1.0);
}
float ao(vec3 p, vec3 n, float t){
float occ = 0.0; float sca = 1.0;
for(int i=0;i<2;i++){
float h = 0.04 + 0.15*float(i);
float d = deScene(p + n*h, t);
occ += (h-d)*sca;
sca *= 0.62;
}
return clamp(1.0 - 2.0*occ, 0.0, 1.0);
}
// dreamy iridescent palette — color depends on phase + normal + wakefulness
vec3 dreamPalette(float phase, float wake, float fres){
vec3 a = vec3(0.55,0.5,0.7);
vec3 b = vec3(0.45,0.4,0.45);
vec3 c = vec3(1.0,0.9,1.3);
vec3 d = vec3(0.30,0.55,0.85) + wake*vec3(0.15,0.05,-0.1);
vec3 col = a + b*cos(TAU*(c*phase*0.15 + d));
col = mix(col, vec3(1.0,0.85,1.0), fres*0.4);
return col;
}
void main(){
vec2 uv = vUv;
float aspect = uRes.x/uRes.y;
vec2 p = uv;
p.x *= aspect;
float t = uTime;
// cinematic drifting orbital camera, gently influenced by mouse
float camAngle = t*0.07 + uMouse.x*0.5;
float camHeight = sin(t*0.05)*0.6 + uMouse.y*0.8;
float camRadius = 6.6 + sin(t*0.035)*0.8;
vec3 ro = vec3(cos(camAngle)*camRadius, camHeight + 1.0, sin(camAngle)*camRadius);
vec3 target = vec3(sin(t*0.025)*0.6, 0.2*sin(t*0.06), cos(t*0.02)*0.4);
vec3 fwd = normalize(target - ro);
vec3 right = normalize(cross(fwd, vec3(0.0,1.0,0.0)));
vec3 up = cross(right, fwd);
float fov = 1.25;
vec3 rd = normalize(fwd*fov + right*p.x + up*p.y);
// raymarch
float dist = 0.0;
float totalDist = 0.0;
bool hit = false;
vec3 pos = ro;
float glowAccum = 0.0;
for(int i=0;i<60;i++){
pos = ro + rd*totalDist;
dist = deScene(pos, t);
glowAccum += exp(-abs(dist)*5.0) * 0.02;
if (dist < 0.003){ hit = true; break; }
if (totalDist > 40.0) break;
totalDist += dist * 0.9;
}
vec3 col = vec3(0.0);
// background: deep dreamy void with layered fog and faint star-noise
vec3 bgA = vec3(0.01,0.012,0.035);
vec3 bgB = vec3(0.06,0.04,0.10);
float bgGrad = 0.5 + 0.5*rd.y;
vec3 bg = mix(bgA, bgB, bgGrad);
float stars = pow(hash21(floor((rd.xy+rd.z*0.3)*400.0)), 28.0);
bg += vec3(stars)*0.6*vec3(0.7,0.8,1.0);
bg += glowAccum * vec3(0.35,0.25,0.55);
if (hit){
vec3 n = calcNormal(pos, t);
vec3 viewDir = -rd;
float fres = pow(1.0 - clamp(dot(n,viewDir),0.0,1.0), 3.0);
// multiple soft emissive light sources roaming through the structure
vec3 lightP1 = vec3(sin(t*0.21)*3.0, cos(t*0.17)*2.0+1.0, cos(t*0.13)*3.0);
vec3 lightP2 = vec3(uMouse*3.0, 1.5);
vec3 l1 = normalize(lightP1 - pos);
vec3 l2 = normalize(lightP2 - pos);
float diff1 = clamp(dot(n,l1),0.0,1.0);
float diff2 = clamp(dot(n,l2),0.0,1.0);
float sh1 = softShadow(pos+n*0.02, l1, t);
float occ = ao(pos, n, t);
vec3 baseCol = dreamPalette(latticeColorPhase, uBloomState, fres);
// subsurface-like translucency approximation
float thickness = clamp(deScene(pos - n*0.3, t), 0.0, 1.0);
vec3 sss = baseCol * (1.0 - thickness) * 0.6;
vec3 lit = baseCol * (0.15 + diff1*0.9*sh1) * vec3(0.9,0.95,1.1);
lit += baseCol * diff2 * 0.5 * vec3(1.0,0.7,0.9);
lit += sss;
lit += fres * vec3(1.0,0.95,1.1) * (0.6 + 0.4*uBloomState);
lit *= occ;
// internal glow / emissive veins via fbm threshold (simplified)
float vein = fbm(pos*3.5 + t*0.15, 2);
float veinMask = smoothstep(0.55, 0.62, vein) * (0.5+0.5*uBloomState);
lit += veinMask * dreamPalette(latticeColorPhase+2.0, uBloomState, 0.0) * 1.4;
// click rupture glow
float cg = clickGlowField(pos, t);
lit += cg * vec3(1.0,0.85,1.2) * 1.6;
col = lit;
// atmospheric depth fog blending toward background
float fog = 1.0 - exp(-totalDist*0.05);
col = mix(col, bg*1.3, fog*0.55);
} else {
col = bg;
}
// soft bloom approximation: boost highlights, gentle vignette
col = col / (1.0 + col*0.4);
vec2 vc = uv;
float vig = 1.0 - 0.25*dot(vc,vc);
col *= vig;
// gentle chromatic dreaminess + tone mapping
col = pow(col, vec3(0.85, 0.88, 0.92));
col += vec3(0.02,0.015,0.03) * (1.0-vig);
// film-like grain to keep it feeling alive/organic
float grain = (hash21(uv*uRes.xy*0.5 + t*60.0) - 0.5) * 0.025;
col += grain;
fragColor = vec4(clamp(col,0.0,1.5), 1.0);
}`;
function compile(type, src){
const sh = gl.createShader(type);
gl.shaderSource(sh, src);
gl.compileShader(sh);
if (!gl.getShaderParameter(sh, gl.COMPILE_STATUS)){
console.error(gl.getShaderInfoLog(sh));
}
return sh;
}
const prog = gl.createProgram();
gl.attachShader(prog, compile(gl.VERTEX_SHADER, vsSrc));
gl.attachShader(prog, compile(gl.FRAGMENT_SHADER, fsSrc));
gl.linkProgram(prog);
if (!gl.getProgramParameter(prog, gl.LINK_STATUS)){
console.error(gl.getProgramInfoLog(prog));
}
gl.useProgram(prog);
const vao = gl.createVertexArray();
gl.bindVertexArray(vao);
const uRes = gl.getUniformLocation(prog, 'uRes');
const uTime = gl.getUniformLocation(prog, 'uTime');
const uMouse = gl.getUniformLocation(prog, 'uMouse');
const uMouseVel = gl.getUniformLocation(prog, 'uMouseVel');
const uBloomState = gl.getUniformLocation(prog, 'uBloomState');
const uClickEnergy = gl.getUniformLocation(prog, 'uClickEnergy[0]');
const uClickPos = gl.getUniformLocation(prog, 'uClickPos[0]');
const uClickTime = gl.getUniformLocation(prog, 'uClickTime[0]');
// ---------- interaction state ----------
let mouseTarget = [0,0];
let mouseSmooth = [0,0];
let mousePrevSmooth = [0,0];
let mouseVelSmooth = [0,0];
const MAX_CLICKS = 8;
let clickEnergy = new Float32Array(MAX_CLICKS);
let clickPos = new Float32Array(MAX_CLICKS*2);
let clickTime = new Float32Array(MAX_CLICKS);
let clickSlot = 0;
// persistent evolving "wakefulness" — grows with interaction, never fully resets
let wakefulness = 0.05;
let interactionScore = 0; // accumulates over the session
window.addEventListener('mousemove', (e)=>{
const x = (e.clientX / window.innerWidth)*2 - 1;
const y = -((e.clientY / window.innerHeight)*2 - 1);
mouseTarget = [x,y];
interactionScore += 0.0008;
hideHint();
}, {passive:true});
window.addEventListener('touchmove', (e)=>{
if (e.touches.length>0){
const t0 = e.touches[0];
const x = (t0.clientX / window.innerWidth)*2 - 1;
const y = -((t0.clientY / window.innerHeight)*2 - 1);
mouseTarget = [x,y];
interactionScore += 0.0008;
}
}, {passive:true});
function spawnClick(nx, ny){
clickEnergy[clickSlot] = 1.0;
clickPos[clickSlot*2] = nx;
clickPos[clickSlot*2+1] = ny;
clickTime[clickSlot] = performance.now()/1000.0;
clickSlot = (clickSlot+1) % MAX_CLICKS;
interactionScore += 0.06;
hideHint();
}
window.addEventListener('mousedown', (e)=>{
const x = (e.clientX / window.innerWidth)*2 - 1;
const y = -((e.clientY / window.innerHeight)*2 - 1);
spawnClick(x,y);
});
window.addEventListener('touchstart', (e)=>{
if (e.touches.length>0){
const t0 = e.touches[0];
const x = (t0.clientX / window.innerWidth)*2 - 1;
const y = -((t0.clientY / window.innerHeight)*2 - 1);
spawnClick(x,y);
}
}, {passive:true});
const hintEl = document.getElementById('hint');
let hintHidden = false;
function hideHint(){
if (!hintHidden && hintEl){
hintHidden = true;
setTimeout(()=>{ hintEl.classList.add('fade'); }, 1800);
}
}
setTimeout(hideHint, 9000);
const startTime = performance.now();
let lastFrame = startTime;
function frame(){
resize();
const now = performance.now();
const dt = Math.min((now - lastFrame)/1000.0, 0.05);
lastFrame = now;
const t = (now - startTime)/1000.0;
// smooth mouse
mouseSmooth[0] += (mouseTarget[0]-mouseSmooth[0]) * Math.min(dt*4.0,1.0);
mouseSmooth[1] += (mouseTarget[1]-mouseSmooth[1]) * Math.min(dt*4.0,1.0);
const vx = (mouseSmooth[0]-mousePrevSmooth[0])/Math.max(dt,0.0001);
const vy = (mouseSmooth[1]-mousePrevSmooth[1])/Math.max(dt,0.0001);
mouseVelSmooth[0] += (vx*0.02 - mouseVelSmooth[0])*0.2;
mouseVelSmooth[1] += (vy*0.02 - mouseVelSmooth[1])*0.2;
mousePrevSmooth[0]=mouseSmooth[0]; mousePrevSmooth[1]=mouseSmooth[1];
// decay click energies
for (let i=0;i<MAX_CLICKS;i++){
if (clickEnergy[i] > 0.0){
clickEnergy[i] -= dt*0.18;
if (clickEnergy[i] < 0.0) clickEnergy[i] = 0.0;
}
}
// wakefulness evolves slowly toward a target derived from accumulated interaction,
// and slowly decays so the piece can rest if left alone — but never fully resets,
// giving the sense of a world remembering the viewer across the session.
interactionScore *= 0.999; // slow forgetting
const target = Math.min(0.15 + interactionScore*1.4, 1.0);
wakefulness += (target - wakefulness) * Math.min(dt*0.5, 1.0);
gl.viewport(0,0,canvas.width, canvas.height);
gl.uniform2f(uRes, canvas.width, canvas.height);
gl.uniform1f(uTime, t);
gl.uniform2f(uMouse, mouseSmooth[0], mouseSmooth[1]);
gl.uniform2f(uMouseVel, mouseVelSmooth[0], mouseVelSmooth[1]);
gl.uniform1f(uBloomState, wakefulness);
gl.uniform1fv(uClickEnergy, clickEnergy);
gl.uniform2fv(uClickPos, clickPos);
gl.uniform1fv(uClickTime, clickTime);
gl.drawArrays(gl.TRIANGLES, 0, 3);
requestAnimationFrame(frame);
}
if (gl) requestAnimationFrame(frame);
</script>
</body>
</html>