Input/Output (I/O) Showcase
Input/Output (I/O)
Table of Contents
- Mermaid Sequence Diagram
- Keyboard Input
- Canvas Rendering
- GameEnv Configuration
- API Integration
- Asynchronous I/O
- JSON Parsing
- I/O in Action
Mermaid Sequence Diagram
This sequence diagram shows what happens when a coin is collected in the Local Storage Demo — from the player triggering an event all the way to the leaderboard updating.
sequenceDiagram
participant Player
participant Coin
participant localStorage
participant API
participant Leaderboard
Player->>Coin: collision detected
Coin->>localStorage: setItem("coinsCollected", n+1)
Coin->>Coin: dispatch coinCollected event
Coin->>API: fetch POST /api/leaderboard
API-->>Coin: 200 OK { status: "ok" }
Coin->>Leaderboard: update display
Leaderboard-->>Player: shows new score
I/O covers how your program interacts with users and other systems, including keyboard input, canvas rendering, and API calls.
Subrequirements
Keyboard Input
- Pong Game: WASD and Arrow key controls (View Pong Game)
- Snake Game: Spacebar and arrow key controls (View Snake Game)
// From hacks/pong.md — Input class captures all key presses with event listeners
class Input {
constructor() {
this.keys = {};
document.addEventListener("keydown", e => {
const k = e.key;
// Prevent default scrolling for game keys
if (k === 'ArrowUp' || k === 'ArrowDown' || k === ' ' || k === 'Spacebar') {
e.preventDefault();
}
this.keys[k] = true;
});
document.addEventListener("keyup", e => {
this.keys[e.key] = false;
});
}
isDown(k) { return !!this.keys[k]; }
}
// From hacks/pong.md — handleInput() uses the Input class to move paddles
handleInput() {
if (this.gameOver) return;
if (this.input.isDown(Config.keys.p1Up)) this.paddleLeft.move(-this.paddleLeft.speed);
if (this.input.isDown(Config.keys.p1Down)) this.paddleLeft.move(this.paddleLeft.speed);
}
Code Runner Challenge
Hit Run to see keyboard event tracking in action. This simulates the same Input class used in Pong — it logs every key state change. Try editing the key list to add your own keys.
Quick Quiz: Keyboard Input
- What JavaScript event is used to detect when a key is pressed?
- a) keydown
- b) keypress
- c) keyup
- Why do we use event listeners for keyboard input in games?
- In the Pong Input class, what does the isDown(k) method check?
Show Answers
1. a) keydown 2. To respond to user actions in real time and make controls interactive. 3. Whether a specific key is currently being held down (true/false).Canvas Rendering
- Pong Game: Drawing paddles, ball, background, and scores
- Snake Game: Drawing snake segments and food on a grid canvas
// From hacks/pong.md — Renderer class wraps all canvas drawing operations
class Renderer {
constructor(ctx) { this.ctx = ctx; }
clear(w, h) {
this.ctx.fillStyle = Config.visuals.bg;
this.ctx.fillRect(0, 0, w, h);
}
rect(r, color = Config.visuals.fg) {
this.ctx.fillStyle = color;
this.ctx.fillRect(r.x, r.y, r.w, r.h);
}
circle(ball, color = Config.visuals.fg) {
this.ctx.fillStyle = color;
this.ctx.beginPath();
this.ctx.arc(ball.position.x, ball.position.y, ball.radius, 0, Math.PI * 2);
this.ctx.closePath();
this.ctx.fill();
}
}
GameEnv Configuration
- Pong Game: Config object for all game settings (View Pong Game)
- Local Storage Demo: Game environment passed as parameter to level constructors
// From hacks/pong.md — full Config object controls every aspect of the game
const Config = {
canvas: { width: 800, height: 500 },
paddle: { width: 10, height: 100, speed: 10.5 },
ball: { radius: 10, baseSpeedX: 5, maxRandomY: 2, spinFactor: 0.3, maxSpeed: 15 },
bumper: { enabledAtScore: 9, radius: 40, color: "#ed1111ff" },
rules: { winningScore: 11 },
visuals: { bg: "#000", fg: "#fff", text: "#fff", gameOver: "red", win: "yellow" }
};
// From _projects/local-storage/levels/localStorageDemo.js — gameEnv config
class localStorageDemo {
constructor(gameEnv) {
let width = gameEnv.innerWidth;
let height = gameEnv.innerHeight;
let path = gameEnv.path;
// environment dimensions drive all sprite and background sizing
}
}
Code Runner Challenge
Hit Run to see a GameEnv-style config object being built and used to create a game setup. This is the same pattern used in Pong and the Local Storage Demo — one config object drives the whole game.
API Integration
fetch. In the Local Storage Demo, we POST scores to a leaderboard API and GET them back to display rankings. Implementing real API calls showed how games connect to the web and share data between players.
- Local Storage Demo: Leaderboard POSTs and GETs scores via fetch (Local Storage Demo)
- Homeworks: JSON Homework
// From _projects/local-storage/levels/localStorageDemo.js
// submitScore sends a POST request with the player name and score
leaderboard.submitScore(playerName, total, 'CoinsCollected').catch(err => {
console.warn('[Leaderboard] submitScore failed:', err);
});
// Leaderboard.submitScore internally uses fetch — POST/GET pattern:
async submitScore(name, score, gameName) {
const response = await fetch('/api/leaderboard', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name, score, gameName })
});
if (!response.ok) throw new Error('HTTP ' + response.status);
return response.json();
}
Code Runner Challenge
Hit Run to see a simulated API integration flow — building a request body, sending it with fetch to a public test API, and handling the response. This mirrors how the Leaderboard in the Local Storage Demo submits scores.
Asynchronous I/O
async/await and Promise chains so the game keeps running while waiting for API responses. Practicing async patterns in the leaderboard and homeworks showed how to build responsive, non-blocking programs.
- Local Storage Demo:
async/awaitin Leaderboard.submitScore,.catch()for error handling (Local Storage Demo) - Homeworks: JSON Homework — fetch and promises
// From _projects/local-storage/levels/localStorageDemo.js — Promise chain
leaderboard.submitScore(playerName, total, 'CoinsCollected').catch(err => {
console.warn('[Leaderboard] submitScore failed:', err);
});
// async/await equivalent — both handle the same asynchronous fetch
async function getScores(gameName) {
try {
const res = await fetch('/api/leaderboard?game=' + gameName);
const data = await res.json();
return data;
} catch (err) {
console.warn('getScores failed:', err);
}
}
Code Runner Challenge
Hit Run to see async/await and Promise chains running side by side. Both patterns handle the same async operation — this is exactly what the leaderboard uses to submit scores without blocking the game loop.
JSON Parsing
JSON.parse() to read incoming data and JSON.stringify() to send it. In the leaderboard and homeworks, parsing JSON responses let us read player scores and display them in the game.
- Local Storage Demo: JSON response from leaderboard API parsed to update score display (Local Storage Demo)
- Homeworks: JSON Homework — JSON.parse and JSON.stringify
// From _notebooks/JavaScriptLessons/JSON — JSON.parse and JSON.stringify
const jsonString = '{"name":"Alex","score":42,"game":"CoinsCollected"}';
const obj = JSON.parse(jsonString);
console.log(obj.name); // "Alex"
console.log(obj.score); // 42
// Object destructuring — cleaner way to pull out API response fields
const { name, score, game } = JSON.parse(jsonString);
console.log(name + " scored " + score + " in " + game);
// JSON.stringify to build request body (used in fetch POST calls)
const payload = JSON.stringify({ name: "Player1", score: 99 });
// → '{"name":"Player1","score":99}'
Code Runner Challenge
Hit Run to see JSON.parse() and JSON.stringify() working together — the exact pattern used when the leaderboard API response arrives and gets turned into a usable JavaScript object. Try changing the score value and run again.
Gamerunner — I/O in Action
Challenge
Snake game showing keyboard input, canvas rendering, and game configuration