class Tunnel implements Effect { private width : number ; private height : number ; private src : ImageData ; private distances : number [ ] = [ ] ; private angles : number [ ] = [ ] ; private shades : number [ ] = [ ] ; public async init ( width , height ) { this . width = width ; this . height = height ; this . src = await demoFX . loadImage ( "space.jpg" ) ; for ( let y = 0 ; y < this . height * 2 ; y ++ ) { const sqy = ( y - this . height ) * ( y - this . height ) ; for ( let x = 0 ; x < this . width * 2 ; x ++ ) { const sqx = ( x - this . width ) * ( x - this . width ) ; this . distances . push ( Math . floor ( 32 * this . src . height / Math . sqrt ( sqx + sqy ) ) % this . src . height ) ; this . angles . push ( Math . round ( this . src . width * Math . atan2 ( y - this . height , x - this . width ) / Math . PI ) ) ; this . shades . push ( Math . min ( Math . sqrt ( sqx + sqy ) , 255 ) ) ; } } } public render ( ctx , t ) { const dest = ctx . getImageData ( 0 , 0 , this . width , this . height ) ; const time = t / 1000 ; const shiftx = Math . floor ( this . src . width * time ) ; const shifty = Math . floor ( this . src . height * 0.25 * time ) ; const centerx = this . width / 2 + Math . floor ( this . width / 4 * Math . sin ( time / 4 ) ) ; const centery = this . height / 2 + Math . floor ( this . height / 4 * Math . sin ( time / 2 ) ) ; const stride = this . width * 2 ; let destOfs = 0 ; for ( let y = 0 ; y < this . height ; y ++ ) { let srcOfs = y * stride + centerx + centery * stride ; for ( let x = 0 ; x < this . width ; x ++ ) { const u = ( this . distances [ srcOfs ] + shiftx ) & 0xff ; let v = ( this . angles [ srcOfs ] + shifty ) % this . src . height ; while ( v < 0 ) { v += this . src . height ; } const shade = this . shades [ srcOfs ] / 255 ; srcOfs ++ ; let pixOfs = ( u + ( v << 8 ) ) << 2 ; dest . data [ destOfs ++ ] = this . src . data [ pixOfs ++ ] * shade ; dest . data [ destOfs ++ ] = this . src . data [ pixOfs ++ ] * shade ; dest . data [ destOfs ++ ] = this . src . data [ pixOfs ++ ] * shade ; dest . data [ destOfs ++ ] = this . src . data [ pixOfs ++ ] ; } } ctx . putImageData ( dest , 0 , 0 ) ; } } demoFX . register ( "tunnel" , new Tunnel ( ) ) ;