Click anywhere to move the spot light. Never let the red dot out of your sight. If you can keep the target within your sight for more than 30 sec, you win.
CR=e=>e.getBoundingClientRect();TN=t=>b.getElementsByTagName(t);PX=s=>s+'px';ST=setTimeout;SQ=n=>n*n;AN=(p,l,s,b)=>`<a style="top:${p}px;left:${p}px;border-radius:${l/2}px;position:absolute;width:${l}px;height:${l}px;transition:all ${s}s;background:#${b};"></a>`;MG=(m)=>M.innerText=m;BG=s=>b.style.background=s;BG('#000');b.style.overflow='hidden';b.innerHTML=AN(0,200,2,"fff")+AN(94,12,15,"a00")+'<b style="float:right;color:#fff;margin:1em;font-size:22px;">';P=TN('a')[0];T=TN('a')[1];M=TN('b')[0];MG('CHASE THE RED DOT');R=125;E=0;onclick=e=>{P.style.top=PX(e.clientY-R);P.style.left=PX(e.clientX-R);};setInterval(()=>{if(!E){p=CR(P);t=CR(T);r=p.width/2;if(SQ(p.top+r-t.top-6)+SQ(p.left+r-t.left-6)>r*r){BG('#a00');T.style.display='none';E=1;MG('YOU LOSE');}}},20);f=(x,y)=>{R-=25;P.style.width=PX(R*2);P.style.height=PX(R*2);T.style.left=x;T.style.top=y;};ST(f,300,'94px','95%');ST(f,10000,'50%','10px');ST(f,18000,'95%','95%');ST(()=>{if(!E){E=1;BG('#048');MG('YOU WIN');}},32000);
Q1I9ZT0+ZS5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtUTj10PT5iLmdldEVsZW1lbnRzQnlUYWdOYW1lKHQpO1BYPXM9PnMrJ3B4JztTVD1zZXRUaW1lb3V0O1NRPW49Pm4qbjtBTj0ocCxsLHMsYik9PmA8YSBzdHlsZT0idG9wOiR7cH1weDtsZWZ0OiR7cH1weDtib3JkZXItcmFkaXVzOiR7bC8yfXB4O3Bvc2l0aW9uOmFic29sdXRlO3dpZHRoOiR7bH1weDtoZWlnaHQ6JHtsfXB4O3RyYW5zaXRpb246YWxsICR7c31zO2JhY2tncm91bmQ6IyR7Yn07Ij48L2E+YDtNRz0obSk9Pk0uaW5uZXJUZXh0PW07Qkc9cz0+Yi5zdHlsZS5iYWNrZ3JvdW5kPXM7QkcoJyMwMDAnKTtiLnN0eWxlLm92ZXJmbG93PSdoaWRkZW4nO2IuaW5uZXJIVE1MPUFOKDAsMjAwLDIsImZmZiIpK0FOKDk0LDEyLDE1LCJhMDAiKSsnPGIgc3R5bGU9ImZsb2F0OnJpZ2h0O2NvbG9yOiNmZmY7bWFyZ2luOjFlbTtmb250LXNpemU6MjJweDsiPic7UD1UTignYScpWzBdO1Q9VE4oJ2EnKVsxXTtNPVROKCdiJylbMF07TUcoJ0NIQVNFIFRIRSBSRUQgRE9UJyk7Uj0xMjU7RT0wO29uY2xpY2s9ZT0+e1Auc3R5bGUudG9wPVBYKGUuY2xpZW50WS1SKTtQLnN0eWxlLmxlZnQ9UFgoZS5jbGllbnRYLVIpO307c2V0SW50ZXJ2YWwoKCk9PntpZighRSl7cD1DUihQKTt0PUNSKFQpO3I9cC53aWR0aC8yO2lmKFNRKHAudG9wK3ItdC50b3AtNikrU1EocC5sZWZ0K3ItdC5sZWZ0LTYpPnIqcil7QkcoJyNhMDAnKTtULnN0eWxlLmRpc3BsYXk9J25vbmUnO0U9MTtNRygnWU9VIExPU0UnKTt9fX0sMjApO2Y9KHgseSk9PntSLT0yNTtQLnN0eWxlLndpZHRoPVBYKFIqMik7UC5zdHlsZS5oZWlnaHQ9UFgoUioyKTtULnN0eWxlLmxlZnQ9eDtULnN0eWxlLnRvcD15O307U1QoZiwzMDAsJzk0cHgnLCc5NSUnKTtTVChmLDEwMDAwLCc1MCUnLCcxMHB4Jyk7U1QoZiwxODAwMCwnOTUlJywnOTUlJyk7U1QoKCk9PntpZighRSl7RT0xO0JHKCcjMDQ4Jyk7TUcoJ1lPVSBXSU4nKTt9fSwzMjAwMCk7
// Click and Chase (JS1k 2018 entry)
//
// A browser game to chase a moving red dot with your mouse. If you can keep
// the target within your sight for more than 30 sec, you win!
//
// Note
// ----
// The key of this game is CSS transition. It introduces some delays between
// user inputs and visual outputs, and makes it hard to navigate the "spot
// light" along with the moving target. This delayed feedback makes this game
// somewhat enjoyable.
// Shorthand functions
CR = e => e.getBoundingClientRect();
TN = t => b.getElementsByTagName(t);
PX = s => s + 'px';
ST = setTimeout;
SQ = n => n * n;
AN = (p,l,s,b) => `<a style="top:${p}px;left:${p}px;border-radius:${l/2}px;position:absolute;width:${l}px;height:${l}px;transition:all ${s}s;background:#${b};"></a>`;
MG = (m) => M.innerText = m;
BG = s => b.style.background = s;
// Set up the game screen
BG('#000');
b.style.overflow = 'hidden';
b.innerHTML = AN(0,200,2,"fff") + AN(94,12,15,"a00") + '<b style="float:right;color:#fff;margin:1em;font-size:22px;">';
P = TN('a')[0];
T = TN('a')[1];
M = TN('b')[0];
MG('CHASE THE RED DOT');
R = 125; // Radius of the spot light
E = 0; // The end-game flag
// Callback for moving the spot light
onclick = e => {
P.style.top = PX(e.clientY - R);
P.style.left = PX(e.clientX - R);
};
// Check if the target remains within player's sight (every 20ms)
setInterval(() => {
if (!E) {
p = CR(P);
t = CR(T);
r = p.width / 2;
if (SQ(p.top + r - t.top - 6) + SQ(p.left + r - t.left - 6) > r * r) {
BG('#a00');
T.style.display = 'none';
E = 1;
MG('YOU LOSE');
}
}
}, 20);
// Define the game scenario
f = (x, y) => {
R -= 25;
P.style.width = PX(R * 2);
P.style.height = PX(R * 2);
T.style.left = x;
T.style.top = y;
};
ST(f, 300, '94px', '95%');
ST(f, 10000, '50%', '10px');
ST(f, 18000, '95%', '95%');
ST(() => {
if (!E) {
E = 1;
BG('#048');
MG('YOU WIN');
}
}, 32000);