04 Sixty
Survive sixty seconds of bullet patterns: spirals, rings with gaps, aimed bursts. Clear it and the next run is harder.
Controls Arrow keys, or drag anywhere on the screen (the dot follows your finger without sitting under it)
988 / 1024 bytes · 36 to spare
The 988 bytes that ship
for(_='0)KterJ7,OA.N=>`/62.5_+60*w^down=onkey&&(t,lf[0]),c.fillText((,p=h,n=/e*2).code]=(t)*l*i0,16,f[1]][e]s&&.client)/aWidth+=320*(eonpoinJ ,t=eX,l=eYNhypot(Rect(Style="#=Nmax(3,Nmin(r=y=15d=[],h=!!x.ArrowlocalStorage.sixty1klet ,e,i,o,n,f,p=s=2,w=x={},A=Math,R=+||S=()`d.length<250&&d.push([9Ncos,Nsin]); e`{x[e1,h-p>40s=K},up=t`x[t move=e`{e.button!(rX-t,yY-l)},setInJval(()`{if(h++,0b0b10",32180e=h/625%3|i=1+w/4,!s){let t=Right-Lef=Down-Up,e=)||1;r31Or+ty17Oy+lh>3749s=360^++)}if(h%[5,530<1)for(o=0;o<[2,1O3;o++)S([.1*h+3.14*o,.3*o+.02*h,Natan2(y-9r-16K+.4*o-.4,[1,.8,1.3);for(f of(ff4f6d",15O8OO7d))+=f[2]+=f[3],-1-1,3,3!-r-y)<2.5s=1p_^,w=K;d=d.filJ(t`t[0]-t[1]-9K<200n>R=R=0|ne9e7df",2!=r-2,y-2,5,5s>2?"CLEAR ×"+w:s>1?"SIXTY":"HIT")+" · TAP",6460-(s?p:h)_).toFixed(1)+" HI "+R,2K},16)';G=/[-^-`NOJK]/.exec(_);)with(_.split(G))_=join(shift());eval(_)Readable source
// SIXTY: survive 60 s of bullet patterns. Harness gives a, b, c.
// d: 0 playing, 1 hit, 2 title, 3 clear. T: frames in run, w: frame the run ended.
let x = 160, y = 150, B = [], T = 0, w = 0, d = 2, n = 0, K = {}, lx, ly, M = Math,
best = +localStorage.sixty1k || 0, ph, r, i, s, q;
// bullet from the (visible) center emitter at angle q, speed v*r; capped
let fire = (q, v) => B.length < 250 && B.push([160, 90, M.cos(q) * v * r, M.sin(q) * v * r]);
onkeydown = onpointerdown = e => {
K[e.code] = 1; lx = e.clientX; ly = e.clientY;
if (d && T - w > 40) x = 160, y = 150, B = [], T = d = 0;
};
onkeyup = e => K[e.code] = 0;
onpointermove = e => {
// relative drag: the dot follows the finger's delta
if (e.buttons && !d) x += (e.clientX - lx) * 320 / a.clientWidth, y += (e.clientY - ly) * 320 / a.clientWidth;
lx = e.clientX; ly = e.clientY;
};
setInterval(() => {
T++;
c.fillStyle = '#0b0b10'; c.fillRect(0, 0, 320, 180);
ph = T / 625 % 3 | 0; r = 1 + n / 4;
if (!d) {
let dx = !!K.ArrowRight - !!K.ArrowLeft, dy = !!K.ArrowDown - !!K.ArrowUp, m = M.hypot(dx, dy) || 1;
x = M.max(3, M.min(317, x + dx / m * 2)); y = M.max(3, M.min(177, y + dy / m * 2));
if (T > 3749) d = 3, w = T, s = 60 + 60 * n++;
}
// patterns rotate every ~10 s: spiral, ring with a gap, aimed burst
if (T % [5, 50, 30][ph] < 1) for (i = 0; i < [2, 17, 3][ph]; i++)
fire([T * .1 + i * 3.14, i * .3 + T * .02, M.atan2(y - 90, x - 160) + i * .4 - .4][ph], [1, .8, 1.3][ph]);
c.fillStyle = '#ff4f6d';
c.fillRect(157, 87, 7, 7);
for (q of B) {
q[0] += q[2]; q[1] += q[3];
c.fillRect(q[0] - 1, q[1] - 1, 3, 3);
if (!d && M.hypot(q[0] - x, q[1] - y) < 2.5) d = 1, w = T, s = w / 62.5 + 60 * n, n = 0;
}
B = B.filter(q => M.hypot(q[0] - 160, q[1] - 90) < 200);
if (s > best) localStorage.sixty1k = best = s | 0;
c.fillStyle = '#e9e7df';
if (d != 2) c.fillRect(x - 2, y - 2, 5, 5);
d && c.fillText((d > 2 ? 'CLEAR ×' + n : d > 1 ? 'SIXTY' : 'HIT') + ' · TAP', 160, 64);
c.fillText((60 - (d ? w : T) / 62.5).toFixed(1) + ' HI ' + best, 160, 20);
}, 16);