Skip to content

Instantly share code, notes, and snippets.

@p01

p01/index.html Secret

Last active February 3, 2024 16:38
Show Gist options
  • Save p01/c2114957168aa478a3dc to your computer and use it in GitHub Desktop.
Save p01/c2114957168aa478a3dc to your computer and use it in GitHub Desktop.

Take 2 of squeezing IMPOSSIBLE ROAD into a JS1k entry

This project was kindly approved by Kevin NG

Test page

Technical notes

The main idea is to go pseudo 3D, make one coordinate ever increasing to simplify the collision detection. The randomness comes from the source code itself which is smoothed out between each "chunk" of road. Bitmasks can be used to toggle which part of the road can vary down the road to increase the difficulty

<!doctype html>
<html>
<head>
<title>JS1k 2015 IMPOSSIBLE ROAD by Mathieu 'p01' Henri</title>
<meta charset="utf-8" />
<meta name="author" content="Mathieu 'p01' Henri, @p01"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<style>
html, body { margin: 0; padding: 0; border: 0; }
#c { display: block; } /* kill scrollbars from hell */
</style>
</head>
<body>
<canvas id="c"></canvas>
<script>
var a = document.getElementsByTagName('canvas')[0];
var b = document.body;
var d = function(e){ return function(){ e.parentNode.removeChild(e); }; }(a);
// unprefix some popular vendor prefixed things (but stick to their original name)
var AudioContext =
window.AudioContext ||
window.webkitAudioContext;
window.requestAnimationFrame || (window.requestAnimationFrame =
window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(f){ setTimeout(f, 1000/30); });
(function() {
var originalRAF = window.requestAnimationFrame;
var time = performance.now();
var framesVBLs = new Array(64);
var frameIndex = 0;
var vblsSum = 0;
window.requestAnimationFrame = function(f) {
var now = performance.now();
var vbls = Math.max(1, Math.round((now - time) * 60 / 1000 - .5));
vblsSum += vbls - (framesVBLs[frameIndex]||0);
framesVBLs[frameIndex] = vbls;
frameIndex = ++frameIndex % framesVBLs.length;
var averageFps = Math.round(60/vblsSum*framesVBLs.length);
c.fillText(averageFps+'fps',16,16);
time = now;
originalRAF(f);
};
})();
// fix bug in safari: http://qfox.nl/weblog/218
document.body.clientWidth;
// auto resize (original) canvas. call `onresize(w,h) to limit the size of the canvas
(window.onorientationchange = window.onresize = function(a){
var mw = Infinity;
var mh = Infinity;
var min = Math.min;
return function(w,h){
if (arguments.length === 2) {
mw = w;
mh = h;
}
a.style.width = (a.width = min(mw, innerWidth)) + 'px';
a.style.height = (a.height = min(mh, innerHeight)) + 'px';
};
}(a))();
var c = a.getContext('2d');
</script>
<script>
// p01: "emulation" of having the source code in window._ as expected after compression
_ = document.querySelector('script').textContent.replace(/\s/g, '');
while(_.length<1024)_+=_;
// onclick=function(){location.href='http://www.p01.org/releases/JS1K_2015_impossible_road/';}
</script>
<script src="test.js"></script>
</body>
</html>
// Compresses down to ~1006b without the debug output
// The "noise" function, "_", will be the minified source code.
// The road is basically an helix with varying radius, angle, and width
// The collision detecion & co can all be done in polar coordinates.
H=96;
z=n=S=f=v=0;
// Offset in the "noise" function to get different roads
F=Date.now()>>9;
// Precalculate the colors
// Setting a #hex fillStyle is much faster than rgb() or hsl()
for(i=444;--i;){
a[i]='#';
for(j=4;--j;) {
a[i]+=Math.min(255,16+(i*5>>j)).toString(16);
}
}
// Test gradient for the title/end screen
g=c.createLinearGradient(0,0,0,8);
g.addColorStop(0,a[96]);
g.addColorStop(1,a[24]);
// Controls
onclick=function(x){
f=1/4-x.clientX/innerWidth/2;
};
onkeydown=function(x){
x=38-x.keyCode;
if(x*x==1)f=x/8;
};
// Main
(down=function(x){
a.height=444;
a.width=444*innerWidth/innerHeight;
requestAnimationFrame(down);
f /= 2;
n += f;
// Compute the components of the road
z+=444.5;
for(i=444;--i;){
z--;
for(j=4;--j;) {
s = z / (32+j*37);
t=(_.charCodeAt(s+F&1023)%(12-j*3))*(s++>=j*4);
u=(_.charCodeAt(s+F&1023)%(12-j*3))*(s++>=j*4);
// smoothstep 5th = x*x*x*(x*(x*6-15)+10);
s%=1;
c[j]=t+(u-t)*s*s*s*(s*(s*6-15)+10);
}
// Draw the road slice in 'i'
L=444/i;
R=c[1]*3;
x=c[2]/2-n;
c.setTransform(Math.cos(x),Math.sin(x),-Math.sin(x),Math.cos(x),a.width/2-L*10*Math.sin(x),160+L*10*Math.cos(x));
s=L*8-L*c[3]*2;
t=L/(2+i/160);
// Draw the road slice in 'i'
c.fillStyle = a[i];
if (z&511) {
c.fillRect(-s,L*H-L*R-t,s*2,t*2);
u=s/9;
s*=z&4?.3:.6;
t++;
} else {
u=c.fillText(z>>9,s,L*H-L*R-t);
}
c.fillStyle = '#fff';
c.fillRect(-u,L*H-L*R-t,-s,t*2);
c.fillRect(u,L*H-L*R-t,s,t*2);
// the road slice where the vessel "sits"
if(i==24) {
q = H + v;
// collision detection to stick the vessel to the road and
// adjust its velocity
if (q < R & q > (R - 2) & x*x < 2 - c[3]/2) {
q = R;
S = z >> 9;
}
v = (q > H ? (q - H) * 1.07 : v) - .07;
v = v < -.5 ? -.5 : v;// > 1 ? 1 :v;
}
}
x=L=0;
c.setTransform(Math.cos(x),Math.sin(x),-Math.sin(x),Math.cos(x),a.width/2-L*10*Math.sin(x),160+L*10*Math.cos(x));
// Score
c.fillStyle=g;
c.fillText(S||'IMPOSSIBLE',0,0);
c.textAlign='center';
c.scale(8,8);
//c.font='96px sans-serif';
H=q;
if(q<-48){
// Game over
c.fillText('AGAIN?',0,8);
} else if (S==0) {
// Game on
c.fillText('ROAD',0,8);
}
// Draw the vessel
c.fillStyle='#fff';
c.shadowColor=a[96];
c.shadowBlur=16;
c.arc(Math.random()*(q<0),16+Math.random()*(q<0),6+q/16,0,16,0);
c.fill();
})()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment