Week 3 — Animation, Inputs & Media
A still picture is not a game. This week the ball moves. You will animate with timers, bounce off walls, let the player change the speed through form inputs, and draw images and video onto the canvas.
🎯 Objectives
- Repeat code over time with
setIntervaland stop it withclearInterval. - Erase and redraw the canvas every frame so motion looks smooth.
- Represent motion with a position (
ballx,bally) and velocity (ballvx,ballvy). - Detect collisions with the walls and reverse velocity.
- Read numbers from form fields and convert with
Number(). - Use HTML5 input validation (
type="number",min,max). - Draw images with
drawImageand embed video with<video>.
🛠 Weekly tasks
- Objective 1 — repeat code over time with
setIntervaland stop it withclearInterval: Activity 1 + buildbouncingballinputs.html. - Objective 2 — erase and redraw the canvas every frame: Activity 2 + build
bouncingballinputs.html. - Objective 3 — represent motion with a position and a velocity: Activity 3 + build
bouncingballinputs.html. - Objective 4 — detect the wall and reverse the velocity: Activity 4 + build
bouncingballinputs.html. - Objective 5 — read numbers from the form with
Number(): Activity 5 + buildbouncingballinputs.html. - Objective 6 — HTML5 input validation (
type="number",min,max): Activity 6 + buildbouncingballinputsvalidate.html. - Objective 7 — draw images with
drawImageand embed video with<video>: Activity 7 + buildbouncingcandybackground.htmlandbouncingVideoOk2.html. - Build your own
bouncingballinputs_yourname.html— using everything you have learned; add gravity by increasingballvya little each frame. (All objectives)
🎓 Lecture activity Open lecture slides →
A 90-minute session of 35 slides. Each concept is taught four ways: the big idea, a short example with its live output, a line-by-line explanation, and a 5-minute “try it” task. The lab work at 1:00 uses the Lab activity below.
Session plan
- 0:0010 minWarm-upReview questions on last week
- 0:1010 minGame & objectivesThis week’s game, objectives and key words
- 0:2035 minConceptsBig idea → code with live output → line by line → try it
- 0:555 minCheck understandingCommon mistakes and a 4-question quiz
- 1:0025 minLabBuild the files in the Lab activity below
- 1:255 minWrap-upBuild-your-own task, marking guide and recap
Concepts this week
- 1. The animation loop — Erase, move, draw, repeat
- 2. Collision & bouncing — Reverse the velocity at a wall
- 3. Input & validation — Check input before you use it
- 4. Gradients & images — Blend colours and stamp pictures
The slides open as a page on this site. Press F for full screen, the arrow keys to move, N for speaker notes, and P to save as PDF (turn on “Background graphics”).
🧪 Lab activity: work through the files Printable lab sheet
Create a folder named Week3 on your PC. Work through the files below
in order, creating each one and writing the code shown. Some files need the images and video
from the Materials panel below. Save each file and refresh the browser.
Want a head start? A scaffold file starter.html is provided — choose it in the
preview dropdown, copy it into your Week3 folder, and complete its TODOs.
-
Create your Week3 folder
On your own PC — do this once before starting.
- Create a folder named
Week3on your PC. - Download the materials below into it (
candy.png,reunion.jpg,pearl.jpg,readers.jpg, thetalkvideo files). - Open your
Week3folder in VS Code or Sublime Text.
- Create a folder named
-
bouncingballinputs.html Objectives 1, 2, 3, 4, 5
Week3\bouncingballinputs.html — a bouncing ball whose speed the player can change.
- [Obj 1, 5] Create
bouncingballinputs.htmland add the page skeleton, the<style>rule for the form, the canvas (with fallback text) and the input form. NoticeonLoad="init();"on<body>andonSubmit="return change();"on the form — these call the functions you write next.<!DOCTYPE html> <html lang="en"> <head> <title>Bouncing Ball with inputs</title> <style> form { width: 330px; margin: 20px; background-color: brown; padding: 20px; } </style> </head> <body onLoad="init();"> <canvas id="canvas" width="400" height="300"> Your browser doesn't support the HTML5 element canvas. </canvas> <br /> <form name="f" id="f" onSubmit="return change();"> Horizontal velocity <input name="hv" id="hv" value="4" type="number" min="-10" max="10" /> <br> Vertical velocity <input name="vv" id="vv" value="8" type="number" min="-10" max="10" /> <input type="submit" value="CHANGE" /> </form> </body> </html> - [Obj 3] Add a
<script>block and declare the box, ball and velocity variables. The four boundary values are calculated from the box and the ball radius, so the ball bounces when its edge (not its centre) reaches the wall.var boxx = 20, boxy = 30, boxwidth = 350, boxheight = 250; var ballrad = 10; var boxboundx = boxwidth + boxx - ballrad; var boxboundy = boxheight + boxy - ballrad; var inboxboundx = boxx + ballrad; var inboxboundy = boxy + ballrad; var ballx = 50, bally = 60; var ctx; var ballvx = 4, ballvy = 8; - [Obj 1, 2] Add
init(). It is called byonLoadon the body: it gets the 2D context, sets the line width and fill colour, draws the first frame, then usessetIntervalto repeatmoveball()every 100 ms (10 frames a second).function init() { ctx = document.getElementById("canvas").getContext("2d"); ctx.lineWidth = ballrad; ctx.fillStyle = "rgb(200,0,50)"; moveball(); setInterval(moveball, 100); } - [Obj 2, 3] Add
moveball()— the frame: erase the old drawing, move the ball, then draw the ball and the box outline again. WithoutclearRectthe old circles would smear across the canvas.function moveball() { ctx.clearRect(boxx, boxy, boxwidth, boxheight); moveandcheck(); ctx.beginPath(); ctx.arc(ballx, bally, ballrad, 0, Math.PI * 2, true); ctx.fill(); ctx.strokeRect(boxx, boxy, boxwidth, boxheight); } - [Obj 3, 4] Add
moveandcheck(). It works out the next position, reverses the velocity when that position crosses a wall, and snaps the ball back onto the wall so it can never escape the box.function moveandcheck() { var nballx = ballx + ballvx; var nbally = bally + ballvy; if (nballx > boxboundx) { ballvx = -ballvx; nballx = boxboundx; } if (nballx < inboxboundx) { nballx = inboxboundx; ballvx = -ballvx; } if (nbally > boxboundy) { nbally = boxboundy; ballvy = -ballvy; } if (nbally < inboxboundy) { nbally = inboxboundy; ballvy = -ballvy; } ballx = nballx; bally = nbally; } - [Obj 5] Add
change(). It reads the two boxes withNumber()and returnsfalseso the form does not reload the page.function change() { ballvx = Number(document.f.hv.value); ballvy = Number(document.f.vv.value); return false; } - Save and refresh: the ball bounces inside the box. Type new values (for example
-6and3) and press CHANGE.
- [Obj 1, 5] Create
-
bouncingballinputsvalidate.html Objective 6
Week3\bouncingballinputsvalidate.html — the same ball, plus HTML5 input validation.
- [Obj 6] Copy
bouncingballinputs.htmltobouncingballinputsvalidate.html. The ball, the canvas and the form stay exactly the same — you only add validation styling, so there is very little new to type. - [Obj 6] Add these two selectors to the
<style>block. A number input becomes:validor:invalidautomatically, based on itstype,minandmax.input:valid { background: green; } input:invalid { background: red; } - [Obj 6] Save and refresh. Both boxes are green, because
4and8are inside the allowed range-10to10. - [Obj 6] Now make a box invalid: type
20(pastmax="10"), clear it, or type letters. It turns red, and pressing CHANGE is blocked by the browser with a validation message —change()never runs. - Try it: add
requiredto one input and submit it empty.<input name="hv" type="number" min="-10" max="10" required />
- [Obj 6] Copy
-
bouncingcandybackground.html Objectives 1, 2, 7
Week3\bouncingcandybackground.html — an image ball bouncing over a photo, with STOP / RESUME.
- [Obj 1, 7] Create
bouncingcandybackground.htmland add the page. The box now fills the whole canvas, and the two<img>tags at the end are hidden byimg { visibility: hidden; }— they are only there so the images start loading.<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>Bouncing cotton candy!</title> <style> form { width: 330px; margin: 20px; background-color: #b10515; padding: 20px; } img { visibility: hidden; } </style> </head> <body onLoad="init();"> Click STOP to stop the cotton candy, then RESUME to start it again. <br /> You can change the velocities too. <br /> <canvas id="canvas" width="400" height="300"> This browser doesn't support the HTML5 canvas element. </canvas> <br /> <form name="f" id="f" onSubmit="return change();"> Horizontal velocity <input name="hv" id="hv" value="4" type="number" min="-10" max="10" /> <br> Vertical velocity <input name="vv" id="vv" value="8" type="number" min="-10" max="10" /> <input type="submit" value="CHANGE" /> <button onClick="return stopcc();">STOP</button> <button onClick="return resume();">RESUME</button> </form> <img src="candy.png" /> <img src="reunion.jpg" /> </body> </html> - [Obj 3, 7] Add the variables and the two images.
stoppedx/stoppedywill remember the speeds while the animation is paused.var ballrad = 10; var boxx = 0, boxy = 0, boxwidth = 400, boxheight = 300; var boxboundx = boxwidth; var boxboundy = boxheight; var inboxboundx = boxx + ballrad; var inboxboundy = boxy + ballrad; var ballx = 50, bally = 60, ballvx = 4, ballvy = 8; var ctx, tid; var bkg = new Image(); var ball = new Image(); var stoppedx = ballvx; var stoppedy = ballvy; - [Obj 1, 7] Add
init(): get the context, set the two image sources, draw the first frame, and remember the timer id so STOP can cancel it.function init() { ctx = document.getElementById("canvas").getContext("2d"); bkg.src = "reunion.jpg"; ball.src = "candy.png"; ctx.lineWidth = ballrad; moveball(); tid = setInterval(moveball, 100); } - [Obj 2, 7] Add
moveball(). The background photo is drawn from a small region into the whole canvas; the candy image is scaled down to388/10by435/10.function moveball() { ctx.clearRect(boxx, boxy, boxwidth, boxheight); moveandcheck(); ctx.drawImage(bkg, 0, 0, 4000, 3000, 0, 0, 400, 300); ctx.drawImage(ball, 0, 0, 388, 435, ballx - ballrad, bally - ballrad, 388 / 10, 435 / 10); ctx.strokeRect(0, 0, 400, 300); } - [Obj 4] Add
moveandcheck()— the same clamped wall bounce you wrote forbouncingballinputs.html.function moveandcheck() { var nballx = ballx + ballvx; var nbally = bally + ballvy; if (nballx > boxboundx) { ballvx = -ballvx; nballx = boxboundx; } if (nballx < inboxboundx) { nballx = inboxboundx; ballvx = -ballvx; } if (nbally > boxboundy) { nbally = boxboundy; ballvy = -ballvy; } if (nbally < inboxboundy) { nbally = inboxboundy; ballvy = -ballvy; } ballx = nballx; bally = nbally; } - [Obj 5] Add
change()(read the inputs and remember the new speeds).function change() { ballvx = Number(document.f.hv.value); ballvy = Number(document.f.vv.value); stoppedx = ballvx; stoppedy = ballvy; return false; } - [Obj 1] Add
stopcc()andresume(). STOP cancels the timer but draws one last frame; RESUME restores the saved speeds and restarts the timer.function stopcc() { clearInterval(tid); stoppedx = ballvx; stoppedy = ballvy; moveball(); return false; } function resume() { clearInterval(tid); ballvx = stoppedx; ballvy = stoppedy; moveball(); tid = setInterval(moveball, 100); return false; } - Save and refresh, then press STOP, change the velocities and press RESUME.
- [Obj 1, 7] Create
-
bouncintballinputsimggradients.html Objectives 2, 3, 7
Week3\bouncintballinputsimggradients.html — an image ball inside rainbow gradient walls.
- [Obj 2, 7] Create
bouncintballinputsimggradients.htmlfrom the same skeleton, but give the page its own styling and form.<!DOCTYPE html> <html lang="en"> <head> <title>Bouncing Ball with inputs</title> <style> form { width: 330px; margin: 20px; background-color: #b10515; padding: 20px; } </style> </head> <body onLoad="init();"> <canvas id="canvas" width="400" height="300"></canvas> <br /> <form name="f" id="f" onSubmit="return change();"> Horizontal velocity <input name="hv" id="hv" value="4" type="number" min="-10" max="10" /> <br> Vertical velocity <input name="vv" id="vv" value="8" type="number" min="-10" max="10" /> <input type="submit" value="CHANGE" /> </form> </body> </html> - [Obj 3, 7] Add the variables, the ball image and the
huearray — each row is one red-green-blue colour of the rainbow.var boxx = 20, boxy = 30, boxwidth = 350, boxheight = 250; var ballrad = 10; var boxboundx = boxwidth + boxx - ballrad; var boxboundy = boxheight + boxy - ballrad; var inboxboundx = boxx + ballrad; var inboxboundy = boxy + ballrad; var ballx = 50, bally = 60, ballvx = 4, ballvy = 8; var ctx, grad, color; var img = new Image(); img.src = "pearl.jpg"; var hue = [ [255, 0, 0], // red [255, 255, 0], // yellow [ 0, 255, 0], // green [ 0, 255, 255], // cyan [ 0, 0, 255], // blue [255, 0, 255] // magenta ]; - [Obj 2] Add
init(). It creates one linear gradient across the box, then loops overhueto add a colour stop for each row, so the walls become a rainbow. Looping over the array means you can add or remove colours without writing new code.function init() { ctx = document.getElementById("canvas").getContext("2d"); grad = ctx.createLinearGradient(boxx, boxy, boxx + boxwidth, boxy + boxheight); for (var h = 0; h < hue.length; h++) { color = "rgb(" + hue[h][0] + "," + hue[h][1] + "," + hue[h][2] + ")"; grad.addColorStop(h * 1 / hue.length, color); } ctx.fillStyle = grad; ctx.lineWidth = ballrad; moveball(); setInterval(moveball, 100); } - [Obj 2, 3, 7] Add
moveball(). It erases the box, moves the ball, draws the pearl image, then paints the four walls withfillRect. The walls pick up the rainbow becausectx.fillStyleis the gradient.function moveball() { ctx.clearRect(boxx, boxy, boxwidth, boxheight); moveandcheck(); ctx.drawImage(img, ballx - ballrad, bally - ballrad, 2 * ballrad, 2 * ballrad); ctx.fillRect(boxx, boxy, ballrad, boxheight); ctx.fillRect(boxx + boxwidth - ballrad, boxy, ballrad, boxheight); ctx.fillRect(boxx, boxy, boxwidth, ballrad); ctx.fillRect(boxx, boxy + boxheight - ballrad, boxwidth, ballrad); } - [Obj 4] Add
moveandcheck()(the same clamped wall bounce).function moveandcheck() { var nballx = ballx + ballvx; var nbally = bally + ballvy; if (nballx > boxboundx) { ballvx = -ballvx; nballx = boxboundx; } if (nballx < inboxboundx) { nballx = inboxboundx; ballvx = -ballvx; } if (nbally > boxboundy) { nbally = boxboundy; ballvy = -ballvy; } if (nbally < inboxboundy) { nbally = inboxboundy; ballvy = -ballvy; } ballx = nballx; bally = nbally; } - [Obj 5] Add
change()for the velocity inputs.function change() { ballvx = Number(document.f.hv.value); ballvy = Number(document.f.vv.value); return false; } - Save and refresh.
- [Obj 2, 7] Create
-
bouncingVideoOk2.html Objectives 2, 7
Week3\bouncingVideoOk2.html — a
<video>that bounces around the page.- [Obj 7] Create
bouncingVideoOk2.html. The video is wrapped in a<div id="con">(this is the element we move), a background photoreaders.jpgdefines the bouncing area, and several<source>formats are given so every browser can play the video.<!DOCTYPE html> <html lang="en"> <head> <title>Bouncing Video</title> <style> #videoE { position: absolute; display: none; z-index: 1; } #con { position: absolute; } </style> </head> <body onLoad="init();"> <image id="AandF" src="readers.jpg" height="100%" /> <div id="con"> <video id="videoE" controls width="300"> <source src="talk.theora.ogv" type="video/ogg" /> <source src="talk.mp4video.mp4" type="video/mp4" /> <source src="talk.webmvp8.webm" type="video/webm" /> Sorry, your browser doesn't support embedded videos. </video> </div> <button onclick="startV()">Click to start</button> </body> </html> - [Obj 7] Add the variables.
var v, c, img; var iwidth, iheight, vwidth, vheight; var leftEdge, rightEdge, topEdge, botEdge; var ballx = 250, bally = 260, ballvx = 14, ballvy = 18; - [Obj 7] Add
init()(called byonLoad). It measures the background image and the video, then works out where the video must turn around — note the bounce area is smaller than the image, because the video has its own width and height.function init() { v = document.getElementById("videoE"); c = document.getElementById("con"); img = document.getElementById("AandF"); iwidth = img.clientWidth; iheight = img.clientHeight; vwidth = v.videoWidth; vheight = v.videoHeight; leftEdge = 5; rightEdge = leftEdge + iwidth - 0.6 * vwidth; topEdge = 5; botEdge = topEdge + iheight - 0.6 * vheight; } - [Obj 7] Add
startV(). It plays and shows the video, places the container, and — when the video ends — rewinds and plays again so it loops forever.function startV() { v.play(); v.style.display = "block"; c.style.top = bally + "px"; c.style.left = ballx + "px"; v.addEventListener("ended", function () { v.currentTime = 0; v.play(); }); moveball(); setInterval(moveball, 100); } - [Obj 2] Add
moveball()andmoveandcheck(). The bounce clamps to the four edges, then the#concontainer is moved with CSStop/left— the same idea as the canvas, but with page elements instead.function moveball() { moveandcheck(); } function moveandcheck() { var nballx = ballx + ballvx; var nbally = bally + ballvy; if (nballx < leftEdge) { ballvx = -ballvx; nballx = leftEdge; } if (nballx > rightEdge) { nballx = rightEdge; ballvx = -ballvx; } if (nbally > botEdge) { nbally = botEdge; ballvy = -ballvy; } if (nbally < topEdge) { nbally = topEdge; ballvy = -ballvy; } ballx = nballx; bally = nbally; c.style.top = bally + "px"; c.style.left = ballx + "px"; } - Save and refresh, then press Click to start: the video plays and bounces around the photo.
- [Obj 7] Create
📝 Tasks for this file:
The heart of the animation is moveandcheck(): it works out the next position,
flips the velocity if that position would cross a wall, then stores the result.
📚 Files in this lab
bouncingballinputs.html— basic ball with speed inputs.bouncingballinputsvalidate.html— adds input validation.bouncingcandybackground.html— image ball on a photo background with STOP / RESUME.bouncintballinputsimggradients.html— gradient styling.bouncingVideoOk2.html— uses the<video>element.