live renderhtml
htmlriemann.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Riemann Zeta - Plain JS + Canvas (Smooth)</title>
<style>
html, body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
background: #000;
overflow: hidden;
}
canvas {
display: block;
position: absolute;
left: 0; top: 0;
}
</style>
</head>
<body>
<canvas id="riemannCanvas"></canvas>
<script>
// ======================================================================
// 1) Basic Utility: map, random, minimal noise2D
// ======================================================================
function mapRange(value, inMin, inMax, outMin, outMax){
return outMin + (outMax - outMin)*((value-inMin)/(inMax-inMin));
}
function lerp(a,b,t){ return a + (b-a)*t; }
function fade(t){ return t*t*t*(t*(t*6 -15)+10); }
function noise2D(x,y){
// minimal "hash" approach
let xi = Math.floor(x), yi= Math.floor(y);
let xf= x-xi, yf= y-yi;
let r1 = randomCell2D(xi, yi);
let r2 = randomCell2D(xi+1, yi);
let r3 = randomCell2D(xi, yi+1);
let r4 = randomCell2D(xi+1, yi+1);
let u= fade(xf), v= fade(yf);
let i1= lerp(r1, r2, u);
let i2= lerp(r3, r4, u);
return lerp(i1, i2, v);
}
function randomCell2D(ix,iy){
let s = (ix*374761393 + iy*668265263)^0xBADC0FFE;
s = (s<<13)^s;
let r= (1.0 - ((s*(s*s*15731+789221)+1376312589)&0x7fffffff)/1073741824.0);
return r;
}
// ======================================================================
// 2) Vec2 class (like p5's createVector)
// ======================================================================
class Vec2 {
constructor(x,y){this.x=x;this.y=y;}
add(v){ return new Vec2(this.x+v.x, this.y+v.y);}
sub(v){ return new Vec2(this.x-v.x, this.y-v.y);}
}
// ======================================================================
// 3) Complex & Zeta
// ======================================================================
function Complex(real, imag){
if(real && real.real!==undefined && real.imag!==undefined) return real;
return {real, imag: imag||0};
}
function complexNeg(a){ return Complex(-a.real, -a.imag);}
function complexMul(a,b){
let re= a.real*b.real - a.imag*b.imag;
let im= a.real*b.imag + a.imag*b.real;
return Complex(re,im);
}
function complexAdd(a,b){
return Complex(a.real+b.real, a.imag+b.imag);
}
function complexSub(a,b){
return Complex(a.real-b.real, a.imag-b.imag);
}
function complexPow(a,b){
// (r e^{i\theta})^(x + i y) = ...
let r= Math.sqrt(a.real*a.real + a.imag*a.imag);
let theta= Math.atan2(a.imag,a.real);
let br= b.real, bi= b.imag;
let logR= Math.log(r);
let x= Math.exp(br*logR - bi*theta);
let phi= bi*logR + br*theta;
return Complex(x*Math.cos(phi), x*Math.sin(phi));
}
function binomial(n, k){
if(k>n)return 0;
if(k> n-k)k=n-k;
let r=1;
for(let i=0;i<k;i++){
r*= (n-i)/(i+1);
}
return r;
}
function sign(k){ return (k%2)? -1: 1; }
function zeta3(s, t=150){
// partial sum approach
// s!=1
// sum_{n=0..t} sum_{k=0..n} [ (-1)^k binomial(n,k) / (k+1)^s ] / 2^(n+1)
// all divided by [1-2^(1-s)]
// from your snippet
let sum= Complex(0,0);
for(let n=0;n<t;n++){
let inn= Complex(0,0);
for(let k=0;k<=n;k++){
let p= complexPow(Complex(k+1,0), complexNeg(s));
let sc= sign(k)* binomial(n,k);
let tmp= complexMul(p, Complex(sc,0));
inn= complexAdd(inn, tmp);
}
let factor= Math.pow(2, -(n+1));
inn= Complex( inn.real*factor, inn.imag*factor );
sum= complexAdd(sum, inn);
}
// factor = 1/(1- 2^(1-s))
let two= Complex(2,0);
let oneMinusS= Complex(1-s.real, -s.imag);
let twoOneS= complexPow(two, oneMinusS);
let denom= complexSub(Complex(1,0), twoOneS);
// sum / denom
// naive complexDiv
let dnorm= denom.real*denom.real + denom.imag*denom.imag;
let conjRe= denom.real, conjIm= -denom.imag;
let cross= complexMul(sum, {real: conjRe, imag: conjIm});
return {
real: cross.real/dnorm,
imag: cross.imag/dnorm
};
}
// ======================================================================
// 4) Riemann Logic w/ Smooth Lines
// We store all zeta points in an array and draw one continuous smooth path
// ======================================================================
class RiemannApp {
constructor(){
this.canvas= document.getElementById('riemannCanvas');
this.ctx= this.canvas.getContext('2d');
this.width=0; this.height=0;
this.resize();
window.addEventListener('resize', ()=>this.resize());
window.addEventListener('keydown', (e)=>this.keyDown(e));
// store zeta coords
this.points= [];
this.index= -0.2;
this.limit= 34;
this.offset=200;
this.start=0;
requestAnimationFrame(()=>this.draw());
}
resize(){
this.width= window.innerWidth;
this.height= window.innerHeight;
this.canvas.width= this.width;
this.canvas.height= this.height;
this.center= new Vec2(this.width*0.5, this.height*0.5);
}
keyDown(e){
if(e.key==='c'){
this.points= [];
this.index= -0.2;
this.limit=34;
} else if(e.key==='p'){
this.limit=64;
}
}
draw(){
requestAnimationFrame(()=>this.draw());
// background
this.ctx.fillStyle= "rgb(6,10,43)";
this.ctx.fillRect(0,0,this.width,this.height);
// generate next zeta point if index < limit
if(this.index< this.limit){
let s= Complex(0.5,this.index);
let comp= zeta3(s);
let x= mapRange(comp.real,-2,2, -this.offset, this.offset);
let y= mapRange(comp.imag,-2,2, -this.offset, this.offset);
// shift by center
x+= this.center.x;
y+= this.center.y;
this.points.push({x,y});
this.index+=0.05;
}
// draw circle etc. if you want
this.ctx.strokeStyle="rgba(255,255,255,0.5)";
this.ctx.beginPath();
this.ctx.arc(this.center.x, this.center.y, 200, 0,Math.PI*2);
this.ctx.stroke();
// draw a smooth curve through all points
if(this.points.length>2){
this.drawCatmullRom();
}
this.start+= 0.0001;
}
drawCatmullRom(){
// We'll do a standard Catmull–Rom approach for a “smooth function.”
// p[i-1], p[i], p[i+1], p[i+2] => we create a segment
let pts= this.points;
this.ctx.lineWidth= 3;
this.ctx.beginPath();
// move to first
this.ctx.moveTo(pts[0].x, pts[0].y);
for(let i=1; i<pts.length-2; i++){
let c1x= (pts[i].x + pts[i+1].x)/2;
let c1y= (pts[i].y + pts[i+1].y)/2;
this.ctx.quadraticCurveTo(pts[i].x, pts[i].y, c1x, c1y);
}
// last segment
let penult= pts[pts.length-2];
let last= pts[pts.length-1];
this.ctx.quadraticCurveTo(penult.x, penult.y, last.x, last.y);
// color logic
// we could pick a color from wave or from length
// for now, a static color
this.ctx.strokeStyle="rgba(255,200,100,0.8)";
this.ctx.stroke();
}
}
// let's go
document.addEventListener("DOMContentLoaded", ()=>{
new RiemannApp();
});
</script>
</body>
</html>