Arcade excitement on display.
Log in to post a comment.
// Forked from "Rasterball" by maraz // https://turtletoy.net/turtle/ebef974b5d const quality = 1.5; // min=0.15, max=5, step=0.15 const letterbox = 0; // min=0.0, max=1.0, step=0.01 const squareSize = 3.275; const perspective = 8.5; const xshift = -87; const yspace = 5; const canvasSize = 1024; const coordSize = 200; const step = coordSize/canvasSize/quality; const halfCanvas = canvasSize/2; const halfCoord = coordSize/2; const topCoord = -halfCoord+letterbox*halfCoord; const bottomCoord = halfCoord-letterbox*halfCoord; const balls = [{ size: 75, xPos: 93, }, { size: 46, xPos: 44.75, }, { size: 29.5, xPos: 7, }, { size: 21.75, xPos: -22.75, }, { size: 17.75, xPos: -55, }, { size: 16, xPos: -84, }] const shadows = balls.map(b => ({ shadow: true, size: b.size*0.75, xPos: b.xPos, yPos: b.size*0.4-1.25/b.size*halfCoord, })) Turtle.prototype.step = function(draw = false) { draw && this.pendown(); this.forward(step); draw && this.penup(); } // This gives us antialiasing for quality > 1. Canvas.setpenopacity(-1+0.0875*(quality-1)); const turtle = new Turtle(); turtle.penup(); turtle.goto(-halfCoord, topCoord); turtle.seth(0); const skyStep = (xl, yl) => Math.abs(yl/3)%2!=0 && Math.abs(xl/3)%2!=0 function sky(x, y, perspective) { const sq = squareSize - 1.5*squareSize*perspective*y/halfCoord/2; const xl = Math.round(x/sq); const yl = Math.round(perspective*y/sq); turtle.step(skyStep(xl, yl)); } const checkCircle = (circle, x, y) => { const ax = Math.abs(x); const ay = Math.abs(y); if (ay < circle.size && ax < circle.size) { const circlePos = Math.sqrt(x*x+y*y); if (circlePos < circle.size) { return circlePos; } } return false; } const checkShadows = (x, y) => { for (const shadow of shadows) { const _x = x - shadow.xPos - xshift; const _y = y - shadow.yPos - yspace; const shadowPos = checkCircle(shadow, _x, _y); if (shadowPos != false) { return { ...shadow, x: _x, y: _y, shadowPos }; } } return false; } const groundStep = (xq, yq) => ((xq+1)%2==0 && (yq+1)%2==0) || (xq%2==0 && yq%2==0); const groundShadowStep = (shadow) => Math.round((Math.random() + shadow.shadowPos/shadow.size)/2) function ground(x, y, perspective) { const sq = 2*squareSize - 2*squareSize*perspective*y/halfCoord/2; const xq = Math.round(x/sq); const yq = Math.round(perspective*y/sq); const shadow = checkShadows(x, y); if (shadow) { turtle.step(groundShadowStep(shadow, xq, yq) && groundStep(xq, yq)); } else { turtle.step(groundStep(xq, yq)); } } const checkBalls = (x, y) => { for (const ball of balls) { const _x = x - ball.xPos; const ballPos = checkCircle(ball, _x, y); if (ballPos != false) { return { ...ball, x: _x, y, ballPos }; } } return false; } const skyReflectionStep = (xq, yq) => Math.abs(yq/11)%2!=0 && Math.abs((xq+12))%5!=0 const fadeStep = (y, intensity, length = intensity*5) => !! Math.round( Math.log10( (length - Math.abs(y)) * Math.random() ) ) % intensity // Conditions for drawing the pattern. const checkerStep = (_ball) => { const sq = _ball.size/10*squareSize - _ball.size/10.6*squareSize*(_ball.ballPos/_ball.size); const xq = Math.round(_ball.x*(1.5)/sq); const yq = Math.round(_ball.y*(1.5)/sq); return ( // Draw the upper part normally. (_ball.y < _ball.size * 0.8) || // For lower part, create a noise shadow. // Increased probability here means lighter shade. Math.round(1-(Math.random()*((_ball.ballPos - 0.81*_ball.size)/(_ball.size*0.25))))==1 ) // Drawing the pattern. && groundStep(xq, yq) } function ball(_ball) { const sq = _ball.size/10*squareSize - _ball.size/10.5*squareSize*(_ball.ballPos/_ball.size); const xq = Math.round(_ball.x*0.5/sq); const yq = Math.round(_ball.y*2/sq); // Sky reflections. if (yq+yspace*(0.12+0.002*_ball.size) < 0 && _ball.y < 0) { turtle.step(skyReflectionStep(xq, yq)); } // Reflection of night sky outside the exhibition. else if (_ball.y < 0) turtle.step(!fadeStep(yq, 2)); // Horizon line. else if (_ball.y === 0) turtle.step(false); // Reflection of ground outside the exhibition. else if (_ball.y > 0 && yq-yspace*(0.12+0.002*_ball.size) < 0) turtle.step(!fadeStep(yq, 5)); // Checkerboard pattern on the balls. else { turtle.step(checkerStep(_ball)); } } const lineMultiplier = 1/quality; let stillMultiplying = 0; let yStep = step/lineMultiplier; function walk(i) { const x = turtle.x(); const y = turtle.y(); if (x > halfCoord) { if (quality < 1) { if (stillMultiplying >= lineMultiplier) { stillMultiplying = 0; } stillMultiplying = stillMultiplying + 1; turtle.goto(-halfCoord, y+yStep); } else { turtle.goto(-halfCoord, y+step); } return true; } const maybeBall = y < 75 && y > -75 && checkBalls(x, y-yStep*stillMultiplying); if (maybeBall) { ball(maybeBall); } else { if (y < 0) { sky(x+xshift, y+yspace, perspective); } else { ground(x+xshift, y-yspace, -perspective); } } return y < bottomCoord; }