Sentinel / index.html
TheWheke's picture
Rename sent_disp_demo.html to index.html
b03e0a8 verified
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Sentinel Demo — Single Timeline Color Cycle</title>
<style>
html,body{
margin:0;
height:100%;
background:#000;
overflow:hidden;
}
canvas{
width:100%;
height:100%;
display:block;
}
</style>
</head>
<body>
<canvas id="gl"></canvas>
<script>
(() => {
const canvas=document.getElementById("gl");
const gl=canvas.getContext("webgl",{antialias:true});
if(!gl){
document.body.innerHTML="WebGL not supported";
return;
}
function resize(){
canvas.width=window.innerWidth;
canvas.height=window.innerHeight;
gl.viewport(0,0,canvas.width,canvas.height);
}
resize();
window.addEventListener("resize",resize);
function compile(type,src){
const s=gl.createShader(type);
gl.shaderSource(s,src);
gl.compileShader(s);
if(!gl.getShaderParameter(s, gl.COMPILE_STATUS)){
throw new Error(gl.getShaderInfoLog(s) || "shader compile failed");
}
return s;
}
function program(v,f){
const p=gl.createProgram();
gl.attachShader(p,compile(gl.VERTEX_SHADER,v));
gl.attachShader(p,compile(gl.FRAGMENT_SHADER,f));
gl.linkProgram(p);
if(!gl.getProgramParameter(p, gl.LINK_STATUS)){
throw new Error(gl.getProgramInfoLog(p) || "program link failed");
}
return p;
}
const quad=gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER,quad);
gl.bufferData(gl.ARRAY_BUFFER,new Float32Array([
-1,-1, 1,-1, -1, 1,
-1, 1, 1,-1, 1, 1
]),gl.STATIC_DRAW);
const VS=`
attribute vec2 a;
varying vec2 v;
void main(){
v=a*0.5+0.5;
gl_Position=vec4(a,0.0,1.0);
}
`;
/* ================================
APPROVED HALO SHADER (ADD-ONLY)
- Added uniform vec3 uCol
- Replaced hard-coded red with uCol
================================= */
const FS=`
precision highp float;
varying vec2 v;
uniform vec2 r;
uniform float t;
/* ADD ONLY */
uniform vec3 uCol;
float ring(vec2 p,float rad){
float d=length(p);
return smoothstep(0.02,0.0,abs(d-rad));
}
void main(){
vec2 uv=v*2.0-1.0;
uv.x*=r.x/r.y;
float time=t*0.8;
float R=0.35+sin(time)*0.01;
float phase=mod(floor(t/5.0),5.0);
if(phase==1.0){
R+=sin(atan(uv.y,uv.x)*6.0+time)*0.005;
}
if(phase==2.0){
R+=sin(length(uv)*40.0-time*2.0)*0.002;
}
if(phase==3.0){
R+=sin(time*6.0)*0.015;
}
if(phase==4.0){
R+=sin(atan(uv.y,uv.x)*80.0+time*4.0)*0.008;
}
float base=ring(uv,R);
float glow=exp(-8.0*abs(length(uv)-R));
/* CHANGED: from vec3 red to uCol */
vec3 color =
uCol*(base*1.4) +
uCol*(glow*0.8);
gl_FragColor=vec4(color,1.0);
}
`;
const prog=program(VS,FS);
const uRes=gl.getUniformLocation(prog,"r");
const uTime=gl.getUniformLocation(prog,"t");
/* ADD ONLY */
const uCol = gl.getUniformLocation(prog,"uCol");
/* ========= ADD ONLY: overlay line program (color capable) ========= */
const LINE_VS=`
attribute vec2 a;
void main(){
gl_Position=vec4(a,0.0,1.0);
}
`;
const LINE_FS=`
precision highp float;
uniform vec3 uColor;
uniform float alpha;
void main(){
gl_FragColor=vec4(uColor,alpha);
}
`;
const lineProg=program(LINE_VS,LINE_FS);
const lineA=gl.getAttribLocation(lineProg,"a");
const lineColor=gl.getUniformLocation(lineProg,"uColor");
const lineAlpha=gl.getUniformLocation(lineProg,"alpha");
const lineBuf=gl.createBuffer();
/* ========= timeline color cycle (single color active per full demo) ========= */
const TIMELINE_COLORS = [
{ name:"red", rgb:[1.00,0.00,0.00] },
{ name:"blue", rgb:[0.10,0.55,1.00] },
{ name:"green",rgb:[0.00,1.00,0.35] },
{ name:"gold", rgb:[1.00,0.78,0.12] }
];
const PHASE_SECONDS = 5;
const PHASE_COUNT = 5;
const DEMO_CYCLE_SECONDS = PHASE_SECONDS * PHASE_COUNT; // 25s
function activeTimelineColor(time){
const completedCycles = Math.floor(time / DEMO_CYCLE_SECONDS);
const idx = completedCycles % TIMELINE_COLORS.length;
return TIMELINE_COLORS[idx].rgb;
}
/* ========= helpers ========= */
function pxToClip(x,y){
return [
(x/canvas.width)*2-1,
1-(y/canvas.height)*2
];
}
function useLine(color, alpha){
gl.useProgram(lineProg);
gl.bindBuffer(gl.ARRAY_BUFFER, lineBuf);
gl.enableVertexAttribArray(lineA);
gl.vertexAttribPointer(lineA,2,gl.FLOAT,false,0,0);
gl.uniform3f(lineColor, color[0], color[1], color[2]);
gl.uniform1f(lineAlpha, alpha);
}
function drawLine(x1,y1,x2,y2,color,alpha){
const p1=pxToClip(x1,y1);
const p2=pxToClip(x2,y2);
useLine(color, alpha);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([p1[0],p1[1], p2[0],p2[1]]), gl.STATIC_DRAW);
gl.drawArrays(gl.LINES, 0, 2);
}
/* ========= scan arm (single color; phase 4 only) ========= */
function drawScanArm(time, color){
const phase = Math.floor(time/PHASE_SECONDS) % PHASE_COUNT;
if(phase !== 4) return;
const angle=time*0.7;
const cx=canvas.width/2;
const cy=canvas.height/2;
const len=Math.max(canvas.width,canvas.height)*1.6;
gl.enable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE);
const tailSteps=18;
for(let i=0;i<tailSteps;i++){
const k=i/(tailSteps-1);
const a=1.0-k;
const tailAngle=angle - k*0.35;
const tx=cx+Math.cos(tailAngle)*len;
const ty=cy+Math.sin(tailAngle)*len;
drawLine(cx,cy,tx,ty,color, 0.02 + a*0.08);
}
const x=cx+Math.cos(angle)*len;
const y=cy+Math.sin(angle)*len;
drawLine(cx,cy,x,y,color,0.36);
gl.disable(gl.BLEND);
}
/* ========= bottom timeline (single color) ========= */
function drawTimeline(time, color){
const padX = Math.max(24, canvas.width*0.08);
const xL = padX;
const xR = canvas.width - padX;
const w = xR - xL;
const y = canvas.height - Math.max(110, canvas.height*0.16);
gl.enable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE);
// base line
drawLine(xL,y,xR,y,color,0.10);
// ticks
const ticks=18;
for(let i=0;i<=ticks;i++){
const x=xL+(i/ticks)*w;
const h=(i%3===0)?8:4;
drawLine(x,y-h,x,y+h,color,0.08);
}
// pulse progresses over the FULL demo cycle (0..1)
const cycleT = (time % DEMO_CYCLE_SECONDS) / DEMO_CYCLE_SECONDS;
const eased = 0.5 - 0.5*Math.cos(cycleT * Math.PI * 2.0);
const px = xL + eased*w;
drawLine(px-14,y,px+14,y,color,0.55);
drawLine(px,y-10,px,y+10,color,0.35);
// trail
for(let i=1;i<=10;i++){
const tx = px - i*10;
if(tx < xL) break;
drawLine(tx-4,y,tx+4,y,color,0.14*(1-i/11));
}
gl.disable(gl.BLEND);
}
/* ========= bind halo ========= */
let aLoc=-1;
function bindHalo(){
gl.useProgram(prog);
gl.bindBuffer(gl.ARRAY_BUFFER,quad);
aLoc = gl.getAttribLocation(prog,"a");
gl.enableVertexAttribArray(aLoc);
gl.vertexAttribPointer(aLoc,2,gl.FLOAT,false,0,0);
}
const start=performance.now();
function draw(){
const time=(performance.now()-start)/1000;
// active single color for entire display (halo + overlays)
const color = activeTimelineColor(time);
gl.clearColor(0,0,0,1);
gl.clear(gl.COLOR_BUFFER_BIT);
bindHalo();
gl.uniform2f(uRes, canvas.width, canvas.height);
gl.uniform1f(uTime, time);
// halo color (ADD ONLY)
gl.uniform3f(uCol, color[0], color[1], color[2]);
gl.drawArrays(gl.TRIANGLES,0,6);
// overlays (same single color)
drawScanArm(time, color);
drawTimeline(time, color);
requestAnimationFrame(draw);
}
draw();
})();
</script>
</body>
</html>