DNF — infrastructure: 9/10 rounds ended in provider errors; its only completed round, R28, scored 8.5
Overview › Physics Lab › R26
Physics Lab
R26 · Projectile ballistics
Archive round — this round predates the blind-first layout, so answers appear with model names attached. Newer rounds keep the models anonymous until you reveal them.
Show task
Create ONE self-contained HTML file (STRICT LIMIT: max 120 lines total) that shows projectile motion on a <canvas>: exactly 5 cannonballs fired simultaneously from the same point at 5 different launch angles (between 20° and 75°) with the same initial speed, constant gravity pulling them down, each ball drawing a fading trail of its trajectory, and distance markers along the ground line showing where each ball lands. When all 5 have landed, restart automatically. Smooth requestAnimationFrame animation. No external libraries, no comments needed. Output ONLY the HTML file content, nothing else.
Answers
Local (Brain cluster)
Show code
<!DOCTYPE html>
<html><head><title></title></head><body><canvas id="c"></canvas><script>const canvas=document.getElementById('c');const ctx=canvas.getContext('2d');canvas.width=800;canvas.height=400;let v0=150;let g=9.8;let startX=50;let angles=[Math.PI * (20+i*23/5)/180 for i in 5];let balls=[];const n=5;for(let i=0;i<n;i++){balls.push({x:startX,y:400-20,vx:v0*Math.cos(angles[i]),vy:v0*Math.sin(angles[i]),history:[],isGrounded:false})}function update(){if(!balls.every(b=>b.isGrounded)){requestAnimationFrame(update)}else{balls.forEach((b,i)=>{b.x=startX;b.y=400-20;b.vx=v0*Math.cos(angles[i]);b.vy=v0*Math.sin(angles[i]);b.history=[]});}ctx.clearRect(0,0,800,400);balls.forEach((b,i)=>{let t=0;if (!b.isGrounded){t+=1/60;const x_new=startX+b.vx*t;const y_new=400-20-(Math.sin(angles[i])*b.vy*t - 0.5*g*t*t);b.x=x_new;b.y=y_new;b.history.push({x:b.x,y:b.y});}else{/*Draw landing marker*/ctx.strokeStyle="red";ctx.lineWidth=2;ctx.beginPath();ctx.moveTo(b.x-50,400-20);ctx.lineTo(b.x+20,400-20);ctx.stroke();}ctx.fillStyle="#ff6347";ctx.beginPath();ctx.arc(b.x,Math.max(10,b.y),5,0,Math.PI*2);ctx.fill();/*Draw trail*/for(let j=0;j<b.history.length;j++){ctx.globalAlpha=(j/(b.history.length)+0.2)*0.8;ctx.fillStyle="#FFAA66";ctx.beginPath();const p=b.history[j];ctx.arc(p.x,p.y,1.5,0,Math.PI*2);ctx.fill();}if (b.isGrounded && Math.abs(b.y-400+20)<2){b.isGrounded=true;b.history=[]}else if (!b.isGrounded){/*Update velocity for next frame projection*/b.vy-=g/60;}});requestAnimationFrame(update)}update();</script></body></html>Show code
```html
<!DOCTYPE html>
<html>
<head>
<title>Projectile Simulation</title>
<style>
body { background: #111; display: flex; justify-content: center; align-items: center; margin: 0; height: 100vh; }
canvas { border: 2px solid #333; background: linear-gradient(to bottom, #6aa84f 0%, #5c9e3b 100%); cursor: crosshair; }
</style>
<script>
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
// Setup Dimensions (Scale to match typical cannon launch)
canvas.width = 800;
canvas.height = 400;
let GRAVITY = 9.81 * 0.15; // Scaled gravity factor for visibility
let V0_SCALE = 1.2; // Initial velocity scale
const GROUND_LEVEL = canvas.height - 50;
const anglesDeg = [20, 35, 45, 60, 70];
let projectiles = [];
let animationFrameId = null;
function initializeProjectiles() {
projectiles = [];
for (let i = 0; i < 5; i++) {
const angleRad = anglesDeg[i] * Math.PI / 180;
projectiles.push({
angle: anglesDeg[i],
x: 20, // Starting X
y: GROUND_LEVEL + 1, // Start just above ground line
vx: Math.cos(angleRad) * V0_SCALE,
vy: -Math.sin(angleRad) * V0_SCALE, // Negative Y because canvas origin is top-left
t: 0,
isCrashed: false,
trail: []
});
}
}
function update() {
let allLanded = true;
projectiles.forEach(p => {
if (p.isCrashed) return;
const dt = 0.1; // Fixed time step for simulation stability
// Update kinematics
p.t += dt;
p.x += p.vx * dt;
p.vy -= GRAVITY * dt; // v_y changes over time (gravity)
p.y += p.vy * dt;
// Store trajectory point for trail drawing
p.trail.unshift({ x: p.x, y: p.y });
// Limit trail length to prevent memory overflow
while (p.trail.length > 150) {
p.trail.pop();
}
// Check collision/landing (Ground level only)
if (p.y >= GROUND_LEVEL - 2 || p.x > canvas.width + 5) {
p.isCrashed = true;
p.y = Math.max(p.y, GROUND_LEVEL); // Snap to ground
p.x = Math.min(p.x, canvas.width * 1.2);
}
if (!p.isCrashed) {
allLanded = false;
}
});
return allLanded;
}
function drawTrails() {
ctx.fillStyle = 'rgba(0, 0, 0, 0)'; // Clear path before drawing new frames
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
function drawProjectilesAndMarkers(allLanded) {
// Draw Ground Line and Markers first
ctx.strokeStyle = '#95a526';
ctx.lineWidth = 3;
ctx.beginPath();
ctx.moveTo(0, GROUND_LEVEL);
ctx.lineTo(canvas.width * 1.2, GROUND_LEVEL);
ctx.stroke();
projectiles.forEach((p, i) => {
// Draw Landing Marker (if crashed)
if (p.isCrashed) {
const markerX = p.x;
ctx.fillStyle = `hsl(${i * 45}, 70%, 60%)`;
ctx.beginPath();
ctx.arc(markerX, GROUND_LEVEL - 1, 8, 0, Math.PI * 2);
ctx.fill();
// Label Angle
ctx.fillStyle = '#333';
ctx.font = '14px Arial';
ctx.fillText(`${p.angle}°`, markerX + 15, GROUND_LEVEL - 10);
} else {
// Draw Current Position (Cannonball)
ctx.fillStyle = `hsl(${i * 45}, 70%, 60%)`;
ctx.beginPath();
ctx.arc(p.x, p.y, 8, 0, Math.PI * 2);
ctx.fill();
// Draw Trail (Fading effect)
if (p.trail.length > 1) {
for (let j = 0; j < Math.min(p.trail.length - 1, 50); j++) {
const point = p.trail[j];
// Fade transparency based on age in the trail array (j=0 is newest, end of array is oldest)
let alpha = (j / Math.min(p.trail.length, 50)) * 0.8;
alpha = Math.pow(1 - (j / p.trail.length), 2); // Quadratic fade
ctx.fillStyle = `rgba(70, 130, 180, ${Math.max(0.05, alpha)})`;
ctx.beginPath();
ctx.arc(point.x, point.y, 2 + (j / p.trail.length) * 4, 0, Math.PI * 2);
ctx.fill();
}
}
}
});
if (allLanded && animationFrameId !== null) {
cancelAnimationFrame(animationFrameId);
document.querySelector('h1') ? document.querySelector('h1').innerHTML = 'Landed!'; : console.log("All landed!");
setTimeout(() => {
initializeProjectiles();
gameLoop();
}, 2000); // Wait 2 seconds before restart
}
}
function gameLoop() {
ctx.clearRect(0, 0, canvas.width * 1.2, canvas.height);
drawTrails();
const allLanded = update();
drawProjectilesAndMarkers(allLanded);
if (!allLanded) {
animationFrameId = requestAnimationFrame(gameLoop);
}
}
// Start the simulation
initializeProjectiles();
document.querySelector('h1') || document.body; // Simple check if placeholder needed
setTimeout(() => gameLoop(), 50); // Delay start slightly
</script>
<div style="text-align: center; padding-top: 20px;"><h1>Cannon Launch Simulation</h1></div>
</body>
</html>
```Show code
<!DOCTYPE html>
<html><head><title>Projectile Motion</title></head><body><canvas id="c"></canvas><script>const c=document.getElementById("c");let ctx=c.getContext("2d");ctx.lineWidth=4;c.width=800;c.height=400;const G=9.8;const V0=15;let balls=[];let time_elapsed=0;const A=[20, 35, 50, 65, 75];function init(){balls=[];time_elapsed=0;$A.forEach((angle,i)=>{balls.push({angle:angle*Math.PI/180,vx:V0*Math.cos(angle*Math.PI/180),vy:V0*Math.sin(angle*Math.PI/180),x:20,y:c.height-50,isActive:true,trail:[{x:20,y:c.height-50}],color:#FF0000,(i+1)*30+(i===4?0:1));});}init();let lastTime=0;function update(time){if(!balls[0]?.isActive && time_elapsed>0){setTimeout(()=>{(balls=[],lastTime=0,update()),500)};return;}const delta=time-lastTime;lastTime=time;for(let i=0;i<balls.length;i++){const b=balls[i];if(!b.isActive)continue;t+=delta/1000;b.x+=b.vx*delta/1000;b.y+=b.vy*delta/1000-(0.5*G*(delta/1000)**2);b.vy-=G*(delta/1000);if(b.y>c.height-1){b.y=c.height-1;b.isActive=false}else{b.trail.push({x:b.x,y:b.y});if(b.trail.length>150)b.trail.shift();}else{b.trail.unshift({x:20+i*30,y:c.height-50});}ctx.fillStyle=getComputedStyle(document.documentElement).getPropertyValue("color").replace('rgb', 'rgba').split(',')[0]';for(let i=0;i<balls.length;i++){const b=balls[i];if(!b||!b.isActive)continue;ctx.beginPath();ctx.strokeStyle=b.color;ctx.lineWidth=3+(1-b.trail.length/200)*3;for(let j=0;j<Math.min(b.trail.length, 50);j++){const p=b.trail[j];ctx.globalAlpha=(j/50);ctx.lineTo(p.x,p.y);ctx.stroke();};ctx.beginPath();ctx.arc(b.x,b.y,4,(getComputedStyle(document.documentElement).getPropertyValue("color").replace('rgb', 'rgba').split(',')[0]'));ctx.fillStyle=getComputedStyle(document.documentElement).getPropertyValue("color");ctx.fill();if(!b.isActive){ctx.fillStyle=b.color;ctx.fillRect(Math.hypot(b.x-20,b.y-(c.height-1)), 0, 50, 10);}}}(time);requestAnimationFrame(update)}</script></body></html>DNF — infrastructure: 10/10 requests returned status 500
Show code
```html
<!DOCTYPE html>
<html>
<head>
<script>
const canvas = document.createElement('canvas');
document.body.appendChild(canvas);
let ctx = canvas.getContext('2d');
canvas.width = 400;
canvas.height = 300;
ctx.fillStyle = 'black';
ctx.fillRect(0,0,canvas.width,canvas.height);
function ball(x,y,angle,vx,vy,distance){
this.x = x;
this.y = y;
this.angle = angle;
this.vx = vx;
this.vy = vy;
this.distanceCovered = distance;
}
ball.prototype.draw = function(){
ctx.beginPath();
ctx.arc(this.x,this.y,3,0,2*Math.PI);
ctx.fillStyle='white';
ctx.fill();
ctx.beginPath();
ctx.moveTo(this.x,this.y);
this.x += this.vx;
this.y += this.vy;
const previousX = this.x - this.vx * 10 / 100;
const previousY = this.y - this.vy * 10 / 100;
if(previousX>0 && previousX<canvas.width){
ctx.lineTo(previousX,previousY);
this.distanceCovered -= Math.sqrt(Math.pow(previousX-this.x,2) + Math.pow(previousY-this.y,2));
ctx.strokeStyle='lightgrey';
ctx.lineWidth=1;
ctx.stroke();
}
}
function animate(){
ctx.fillStyle='#000000';
ctx.fillRect(0,canvas.height-10,canvas.width,canvas.height)
let balls = [];
const speed = 3.5; // initial speed of the projectile in pixels per second
const g = 2.5; // constant acceleration due to gravity by second
for(let j=0;j<5;j++){
const distanceCovered = j * canvas.width / 4;
balls.push(new ball(50,150 + (canvas.height - 10)/2, Math.PI*(35+(j*15)),
speed*Math.cos(((Math.PI)*3/180)*(20+j*15)) ,
-(speed*g+0.1*Math.sqrt((distanceCovered))*Math.sin(((Math.PI)*3/180)*(20+j*15))), distanceCovered));
}
for(let j=0;j<balls.length;j++){
balls[j].draw();
balls[j].x += balls[j].vx;
balls[j].y += balls[j].vy;
if((balls[j].y>canvas.height) || (Math.sqrt(Math.pow(balls[j].x,2) + Math.pow(balls[j].y - 150,2)) > canvas.width/2)){
delete balls[j];
}else{
ctx.beginPath();
ctx.font="10pt Arial";
const xx = balls[j].y<canvas.height?balls[j].x-5:balls[j].distanceCovered*Math.sin(Math.atan((balls[j].y-canvas.height)/(canvas.width/2)));
const yy = canvas.height - 5;
ctx.fillText(25*j + '°',xx,yy)
}
}
if(balls.some(ball => ball.y > -10 || ball.distanceCovered < canvas.width/2)){
globalThis.requestAnimationFrame(animate); // animate()
}else{
ctx.fillStyle='black';
ctx.fillRect(0, 0, canvas.width,canvas.height),
ctx.font = "36pt Arial";
ctx.textAlign = 'center',
ctx.textBaseline = 'middle'
ctx.fillText(`CANNONBALLS LANDED... AUTO RELAUNCH`,canvas.width/2,-50)
for (let x=25; x<=225;x+=25)
textShadow = 0, this.ctx.fillStyle='lightgrey',this.ctx.fillText(x+'°',x+10,250)
ctx.font = "48pt Arial";
ctx.textAlign = 'center',
ctx.textBaseline = 'middle'
ctx.fillText('CLICK TO RELAUNCH...',canvas.width/2,-50),
}
}
globalThis.requestAnimationFrame(animate);
</script>
</head>
</html>
```Show code
```html
<!DOCTYPE html>
<html><head><title>Projectile Motion</title><style>body{margin:0}. canvas{border:1px solid black;}</style></head>
<body><canvas id="c" width=800 height=400></canvas>
<script>
const c = document.getElementById('c'), ctx=c.getContext('2d');
const W=800,H=400,g=0.15,v0=30,angles=[20,30,40,60,75];
let cannonballs=[]; // Each is {x,y,alpha,trailPoints:[]}
for(let a=0;a<angles.length;a++){
const angle=angles[a]*Math.PI/180;
cannonballs.push({
x:W/2,
y:H-30,
a:angle,
trail:[[W/2,H-30]]
});
}
let allLanded = false, frameCount=0;
function updateCannonballs(){
if(allLanded){
cannonballs.forEach(c=>{
c.x=c.x0; c.y=c.y0;
c.trail=[c.x0,c.y0];
});
allLanded=false;
return true;
}
}
function animate() {
frameCount++;
ctx.clearRect(0,0,W,H);
for(let i=0;i<cannonballs.length;i++){
let cb = cannonballs[i];
if(frameCount>5)
cb.trail.push([cb.x,cb.y]);
const t=(frameCount)/30;
const vx=v0*Math.cos(cb.a);
const vy=v0*Math.sin(cb.a)-g*t;
cb.x=x0 + vx*t;
c.y=y0 + (vy*t - 0.5*g*t*t);
if(c y> H-30){
allLanded = cannonballs.every(c=>c.y>=H-30);
cb.x= x0; cb.y= y0;
cb.trail=[x0,y0];
}
}
drawCannonballs();
}
function drawCannonballs() {
ctx.beginPath();
for(let i=0;i<cannonballs.length;i++){
let cb = cannonballs[i];
trailPoints.forEach(p => drawLine(ctx, p[0],p[1]));
ctx.beginPath();
ctx.arc(cb.x,cb.y,5,0,Math.PI*2);
ctx.fillStyle='blue'; ctx.fill();
ctx.strokeStyle=$('#666', 2); ctx.stroke();
}
}
function drawLine(ctx,x,y) {
if(trailPoints.length>1){
trailPoints.forEach((p,j)=>j?Ctx.beginPath();draw points j-1 and j}
});
}
animate();
</script>
</body></html>
```Show code
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Projectile Motion</title>
<style>
canvas { background: #f0f0f0; }
</style>
</head>
<body>
<canvas id="cannonCanvas" width="600" height="400"></canvas>
<script>
const ctx = document.getElementById('cannonCanvas').getContext('2d');
const g = 9.8;
const u = 50; // initial speed
let balls = [];
function start(){
balls = [];
const angles = [20,35,45,60,75];
for (let angle of angles) {
let radians = angle * Math.PI / 180;
balls.push({
x: u * Math.cos(radians),
y: -u * Math.sin(radians) + g,
theta: radians
});
}
}
function animate(){
ctx.clearRect(0,0,600,400);
for (let ball of balls){
if (ball.y > 350 || ball.x < -10){
continue; // Ball out of view.
}
ctx.beginPath();
ctx.arc(150 + ball.x, 387-ball.y, 2, 0, Math.PI * 6);
if (ball.opacity>0){
let alpha = ball.alpha/10;
let gradient = ctx.createRadialGradient(
150 + ball.x,
400 - ball.y,
8,
150 +ball.x, 387-ball.y,
2
);
graduate.addColorStop(1, 'rgba(0,0,255,'+alpha+')');
ctx.fillStyle=gradient;
}else{
ctx.fillStyle = "#FF0000";
}
ball.opacity --;
}ctx.fill();
ball.y += 14 *ball.alpha / 10;
}
function update(){
for (let ball of balls){
if(ball.x>300||ball.y>-350 ) continue; else{
ctx.font='bold 12pt Calibri';
ctx.fillText(Math.round((Math.sin(bx*Math.atan(.9487)+(Math.pow(-2*(b.ang/2), .0764)))))
+(Math.sqrt(ball.alpha/(15))-0.1) )) ;
}
for (let ball of balls){
ball.x += 0.33*ball.alpha;
}
}
function end(){
if(balls.every(b => b.y >= 387)) start(); // Ball landed.
requestAnimationFrame(end);
update();
}
start();
end();
</script>
</body>
</html>```Cloud
Show code
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Projectile Motion</title>
<style>
html,body{margin:0;height:100%;overflow:hidden;background:#101820;font-family:Arial,sans-serif}
canvas{display:block;width:100vw;height:100vh}
</style>
</head>
<body>
<canvas id="c"></canvas>
<script>
const c=document.getElementById("c"),x=c.getContext("2d");
let W,H,DPR,ground,start,balls,last=0,scale=0.8;
const angles=[20,34,48,62,75],colors=["#ff595e","#ffca3a","#8ac926","#1982c4","#b5179e"];
const v0=360,g=420;
function resize(){
DPR=devicePixelRatio||1; W=innerWidth; H=innerHeight;
c.width=W*DPR; c.height=H*DPR; x.setTransform(DPR,0,0,DPR,0,0);
ground=H-70; start={x:60,y:ground}; reset();
}
function reset(){
balls=angles.map((a,i)=>{
let r=a*Math.PI/180;
return {a,i,x:start.x,y:start.y,vx:v0*Math.cos(r),vy:-v0*Math.sin(r),trail:[],landed:false,landX:null,t:0};
});
last=performance.now();
}
function drawGrid(){
x.strokeStyle="#2d3b45"; x.lineWidth=1;
x.beginPath(); x.moveTo(0,ground); x.lineTo(W,ground); x.stroke();
x.fillStyle="#d8e2e8"; x.font="14px Arial";
x.fillText("0 m",start.x-8,ground+26);
for(let m=100;m<900;m+=100){
let px=start.x+m*scale;
if(px>W-30) break;
x.strokeStyle="#51616c"; x.beginPath(); x.moveTo(px,ground-6); x.lineTo(px,ground+6); x.stroke();
x.fillStyle="#9fb1bc"; x.fillText(m+" m",px-18,ground+26);
}
}
function cannon(){
x.fillStyle="#c6d2da"; x.beginPath(); x.arc(start.x,start.y,11,0,Math.PI*2); x.fill();
x.fillStyle="#8797a1"; x.fillRect(start.x-42,start.y+12,70,14);
}
function step(now){
let dt=Math.min((now-last)/1000,.033); last=now;
x.clearRect(0,0,W,H);
let grd=x.createLinearGradient(0,0,0,H);
grd.addColorStop(0,"#162634"); grd.addColorStop(1,"#0d141a");
x.fillStyle=grd; x.fillRect(0,0,W,H);
drawGrid(); cannon();
let all=true;
for(const b of balls){
if(!b.landed){
b.t+=dt; b.x=start.x+b.vx*b.t*scale; b.y=start.y+(b.vy*b.t+.5*g*b.t*b.t)*scale;
b.trail.push({x:b.x,y:b.y});
if(b.trail.length>70)b.trail.shift();
if(b.y>=ground){b.y=ground;b.landed=true;b.landX=b.x;b.trail.push({x:b.x,y:b.y});}
}
if(!b.landed)all=false;
for(let i=1;i<b.trail.length;i++){
x.strokeStyle=colors[b.i]+Math.floor(255*i/b.trail.length).toString(16).padStart(2,"0");
x.lineWidth=3; x.beginPath(); x.moveTo(b.trail[i-1].x,b.trail[i-1].y); x.lineTo(b.trail[i].x,b.trail[i].y); x.stroke();
}
x.fillStyle=colors[b.i]; x.beginPath(); x.arc(b.x,b.y,7,0,Math.PI*2); x.fill();
if(b.landed){
x.strokeStyle=colors[b.i]; x.beginPath(); x.moveTo(b.landX,ground-16); x.lineTo(b.landX,ground+16); x.stroke();
x.fillStyle=colors[b.i]; x.font="13px Arial";
x.fillText(Math.round((b.landX-start.x)/scale)+" m",b.landX-22,ground-22-14*b.i);
}
}
if(all)setTimeout(reset,900);
requestAnimationFrame(step);
}
addEventListener("resize",resize);
resize();
requestAnimationFrame(step);
</script>
</body>
</html>Show code
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Projectile Motion</title>
<style>
body { margin: 0; overflow: hidden; background: #1a1a1a; }
canvas { display: block; }
</style>
</head>
<body>
<canvas id="c"></canvas>
<script>
const canvas = document.getElementById('c');
const ctx = canvas.getContext('2d');
canvas.width = 1200;
canvas.height = 600;
const g = 0.5;
const v0 = 15;
const angles = [20, 35, 50, 65, 75];
let balls = [];
let time = 0;
let allLanded = false;
function init() {
balls = angles.map(angle => {
const rad = angle * Math.PI / 180;
return {
x: 50,
y: 550,
vx: v0 * Math.cos(rad),
vy: -v0 * Math.sin(rad),
trail: [],
color: `hsl(${angle * 3}, 100%, 60%)`,
landed: false
};
});
time = 0;
allLanded = false;
}
function update() {
allLanded = true;
balls.forEach(ball => {
if (!ball.landed) {
ball.vy += g;
ball.x += ball.vx;
ball.y += ball.vy;
ball.trail.push({x: ball.x, y: ball.y});
if (ball.trail.length > 50) ball.trail.shift();
if (ball.y >= 550) {
ball.y = 550;
ball.landed = true;
} else {
allLanded = false;
}
}
});
if (allLanded && time > 60) init();
time++;
}
function draw() {
ctx.fillStyle = '#1a1a1a';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.strokeStyle = '#666';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(0, 550);
ctx.lineTo(1200, 550);
ctx.stroke();
for (let i = 0; i <= 12; i++) {
const x = i * 100;
ctx.strokeStyle = '#444';
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(x, 545);
ctx.lineTo(x, 555);
ctx.stroke();
ctx.fillStyle = '#888';
ctx.font = '12px monospace';
ctx.fillText(i * 100, x - 15, 575);
}
balls.forEach(ball => {
ctx.strokeStyle = ball.color;
ctx.globalAlpha = 0.3;
ctx.lineWidth = 2;
ctx.beginPath();
ball.trail.forEach((p, i) => {
if (i === 0) ctx.moveTo(p.x, p.y);
else ctx.lineTo(p.x, p.y);
});
ctx.stroke();
ctx.globalAlpha = 1;
ctx.fillStyle = ball.color;
ctx.beginPath();
ctx.arc(ball.x, ball.y, 5, 0, Math.PI * 2);
ctx.fill();
});
}
function loop() {
update();
draw();
requestAnimationFrame(loop);
}
init();
loop();
</script>
</body>
</html>Show code
<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>Projectiles</title>
<style>body{margin:0;background:#0a0e1a;overflow:hidden}canvas{display:block}</style></head>
<body><canvas id="c"></canvas>
<script>
const cvs=document.getElementById('c'),ctx=cvs.getContext('2d');
function resize(){cvs.width=innerWidth;cvs.height=innerHeight}
resize();addEventListener('resize',resize);
const g=400,v0=280,angles=[20,35,50,65,75];
const colors=['#ff5555','#ffb347','#ffe066','#66ff99','#66ccff'];
let originX,groundY,balls,t0,restarting=false;
function init(){
originX=80;groundY=cvs.height-60;
balls=angles.map((a,i)=>({a:a*Math.PI/180,color:colors[i],trail:[],landed:false,landX:0}));
t0=performance.now();restarting=false;
}
init();
function frame(now){
const t=(now-t0)/1000;
ctx.fillStyle='rgba(10,14,26,0.25)';
ctx.fillRect(0,0,cvs.width,cvs.height);
ctx.strokeStyle='#334';ctx.beginPath();
ctx.moveTo(0,groundY);ctx.lineTo(cvs.width,groundY);ctx.stroke();
let allLanded=true;
balls.forEach(b=>{
const vx=v0*Math.cos(b.a),vy=v0*Math.sin(b.a);
let x=originX+vx*t, y=groundY-(vy*t-0.5*g*t*t);
if(t>0.05 && y>=groundY && !b.landed){b.landed=true;b.landX=x;}
if(b.landed){x=b.landX;y=groundY;} else allLanded=false;
b.trail.push({x,y});
if(b.trail.length>400)b.trail.shift();
for(let i=0;i<b.trail.length;i++){
const p=b.trail[i];
ctx.globalAlpha=i/b.trail.length;
ctx.fillStyle=b.color;
ctx.fillRect(p.x-1.5,p.y-1.5,3,3);
}
ctx.globalAlpha=1;
ctx.beginPath();ctx.arc(x,y,6,0,7);ctx.fillStyle=b.color;ctx.fill();
if(b.landed){
ctx.strokeStyle=b.color;
ctx.beginPath();ctx.moveTo(b.landX,groundY-10);ctx.lineTo(b.landX,groundY+10);ctx.stroke();
ctx.fillStyle=b.color;ctx.font='12px sans-serif';
ctx.fillText(Math.round(b.landX-originX)+'m',b.landX-15,groundY+25);
}
});
ctx.beginPath();ctx.arc(originX,groundY,5,0,7);ctx.fillStyle='#fff';ctx.fill();
if(allLanded && t>1 && !restarting){restarting=true;setTimeout(init,1200);}
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
</script></body></html>Show code
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Projectile Motion</title>
<style>html,body{margin:0;background:#0b1020;overflow:hidden}canvas{display:block}</style>
</head>
<body>
<canvas id="c"></canvas>
<script>
const cv=document.getElementById('c'),ctx=cv.getContext('2d');
let W,H,ground;
function resize(){W=cv.width=innerWidth;H=cv.height=innerHeight;ground=H-60;}
addEventListener('resize',resize);resize();
const angles=[20,34,48,61,75],speed=Math.min(W,H)*0.9,g=400,ox=80;
const colors=['#ff5a5f','#ffb703','#8ecae6','#90ee90','#c77dff'];
let balls=[];
function reset(){oy=ground;balls=angles.map((a,i)=>{const r=a*Math.PI/180;return{x:ox,y:ground,vx:Math.cos(r)*speed,vy:-Math.sin(r)*speed,trail:[],landed:false,land:0,c:colors[i]};});}
let oy;reset();
let last=performance.now();
function step(t){const dt=Math.min((t-last)/1000,0.033);last=t;
ctx.fillStyle='#0b1020';ctx.fillRect(0,0,W,H);
ctx.strokeStyle='#334';ctx.beginPath();ctx.moveTo(0,ground);ctx.lineTo(W,ground);ctx.stroke();
let allLanded=true;
for(const b of balls){
if(!b.landed){allLanded=false;
b.vy+=g*dt;b.x+=b.vx*dt;b.y+=b.vy*dt;
b.trail.push({x:b.x,y:b.y});if(b.trail.length>120)b.trail.shift();
if(b.y>=ground){b.y=ground;b.landed=true;b.land=b.x;}
}
for(let i=0;i<b.trail.length;i++){const p=b.trail[i];ctx.globalAlpha=i/b.trail.length*0.7;ctx.fillStyle=b.c;ctx.beginPath();ctx.arc(p.x,p.y,2,0,7);ctx.fill();}
ctx.globalAlpha=1;ctx.fillStyle=b.c;ctx.beginPath();ctx.arc(b.x,b.y,7,0,7);ctx.fill();
if(b.landed){ctx.strokeStyle=b.c;ctx.beginPath();ctx.moveTo(b.land,ground);ctx.lineTo(b.land,ground+18);ctx.stroke();
ctx.fillStyle=b.c;ctx.font='12px sans-serif';ctx.fillText(Math.round((b.land-ox)/10)+'m',b.land-10,ground+34);}
}
if(allLanded){if(!balls.doneAt)balls.doneAt=t;if(t-balls.doneAt>1200)reset();}
requestAnimationFrame(step);}
requestAnimationFrame(step);
</script>
</body>
</html>
Show code
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Projectile Motion</title>
<style>html,body{margin:0;height:100%;background:#0b1020;overflow:hidden}canvas{display:block}</style>
</head>
<body>
<canvas id="c"></canvas>
<script>
const cv=document.getElementById('c'),ctx=cv.getContext('2d');
let W,H,oy;const ox=60,G=280;
const angles=[20,34,45,60,75],cols=['#ff5252','#ffb300','#66bb6a','#29b6f6','#e040fb'];
let balls=[],waitT=0;
function launch(){const v0=Math.sqrt(0.85*(W-120)*G);
balls=angles.map((a,i)=>{const r=a*Math.PI/180;
return{x:ox,y:oy,vx:v0*Math.cos(r),vy:-v0*Math.sin(r),c:cols[i],ang:a,landed:false,lx:0};});
waitT=0;ctx.fillStyle='#0b1020';ctx.fillRect(0,0,W,H);}
function resize(){W=cv.width=innerWidth;H=cv.height=innerHeight;oy=H-60;launch();}
resize();addEventListener('resize',resize);
let last=performance.now();
function frame(t){requestAnimationFrame(frame);
const dt=Math.min((t-last)/1000,0.033);last=t;
ctx.fillStyle='rgba(11,16,32,0.05)';ctx.fillRect(0,0,W,H);
ctx.strokeStyle='#8899aa';ctx.lineWidth=2;ctx.beginPath();ctx.moveTo(0,oy);ctx.lineTo(W,oy);ctx.stroke();
ctx.fillStyle='#cfd8dc';ctx.beginPath();ctx.arc(ox,oy,8,0,7);ctx.fill();
let all=true;
for(const b of balls){
if(!b.landed){all=false;b.vy+=G*dt;b.x+=b.vx*dt;b.y+=b.vy*dt;
if(b.y>=oy&&b.vy>0){b.y=oy;b.landed=true;b.lx=b.x;}}
ctx.fillStyle=b.c;ctx.beginPath();ctx.arc(b.x,b.y,6,0,7);ctx.fill();
if(b.landed){ctx.strokeStyle=b.c;ctx.lineWidth=2;
ctx.beginPath();ctx.moveTo(b.lx,oy);ctx.lineTo(b.lx,oy+14);ctx.stroke();
ctx.fillStyle=b.c;ctx.font='12px monospace';ctx.textAlign='center';
ctx.fillText(b.ang+'°',b.lx,oy+30);
ctx.fillText(Math.round((b.lx-ox)/10)+'m',b.lx,oy+44);}}
if(all){waitT+=dt;if(waitT>1.6)launch();}}
requestAnimationFrame(frame);
</script>
</body>
</html>