← Home    Full Blog

Input/Output

Keyboard Input · Canvas Rendering · GameEnv Configuration · API Integration · Asynchronous I/O · JSON Parsing

📋 Table of Contents

  1. Keyboard Input
  2. Canvas Rendering
  3. GameEnv Configuration
  4. API Integration
  5. Asynchronous I/O
  6. JSON Parsing

⌨️ Keyboard Input

Event Listeners

Keyboard events are captured with addEventListener on the document. The SPACE key triggers laser attacks; WASD is handled by the engine's Player class.

// Keyboard event listener registered in initialize()
this.boundKeyDown = this.handleKeyDown.bind(this);
document.addEventListener('keydown', this.boundKeyDown);

handleKeyDown(event) {
  if (event.code === 'Space') {
    this.attackRequested = true;   // Boolean flag set on keypress
    event.preventDefault();         // stop page from scrolling
  }
}

// Listener is cleaned up in destroy() to prevent memory leaks
document.removeEventListener('keydown', this.boundKeyDown);

Code Runner Challenge

Simulate a key event system and nested condition logic like your play attack cooldown.

Lines: 1Characters: 0
Output
Click "Run" in code control panel to see output ...

🖼️ Canvas Rendering

draw() via GameEngine

All characters are drawn to the HTML5 Canvas each frame. The draw() method is inherited from GameObject and called inside the overridden update(). The hitbox overlay uses the Canvas 2D context directly.

// draw() is inherited — called every frame inside update()
update() {
  this.draw();   // Canvas API — renders sprite to canvas element
  // ...
}

// Direct Canvas API usage in drawHitbox()
const ctx = this.gameEnv?.ctx;
ctx.strokeRect(this.position.x, this.position.y, this.width, this.height);

Try It — Canvas Rendering

Canvas API methods can't run in a code runner (no DOM), but this simulates the draw call sequence so you can see what happens each frame.

Lines: 1Characters: 0
Output
Click "Run" in code control panel to see output ...

⚙️ GameEnv Configuration

Canvas Size, Level Classes & Server URIs

The game environment is initialized in the main entry file. Canvas size, level classes, and server URIs are all passed in via the environment object.

// 03-v1-1-PeppaPig.md — game entry point
const environment = {
  path:           "{{site.baseurl}}",
  pythonURI:      pythonURI,               // String — backend server
  javaURI:        javaURI,                 // String — backend server
  fetchOptions:   fetchOptions,            // Object — shared fetch config
  gameContainer:  document.getElementById("gameContainer"),
  gameCanvas:     document.getElementById("gameCanvas"),
  gameLevelClasses: [PeppaLevel1, PeppaLevel2, PeppaLevel3]   // Array
};

Game.main(environment);   // launches the game loop

Try It — GameEnv Configuration

Run this to see how the config object drives the game. Try reordering levelClasses to change the level sequence.

Lines: 1Characters: 0
Output
Click "Run" in code control panel to see output ...

🌐 API Integration

An API (Application Programming Interface) is a way for your code to talk to an external service over the internet. You send a request, the service sends back data — usually as JSON. In the game, this same pattern is used to save scores and fetch leaderboard data. To show how APIs work in a fun way, I used the public iTunes Search API to search for songs and play previews directly in the browser.

How the iTunes API Works

The iTunes Search API is free and requires no API key. You send a GET request with a search term, and it returns a JSON object with an array of matching tracks — each one includes the song name, artist, album art, and a 30-second previewUrl you can feed directly to an Audio object.

// Step 1 — build the request URL with a search term
const url = 'https://itunes.apple.com/search?term=every+breath+you+take+police&entity=song&limit=1';

// Step 2 — fetch() sends the HTTP GET request (async — we must await it)
const response = await fetch(url);

// Step 3 — parse the JSON body (also async)
const data = await response.json();

// Step 4 — pull the first result out of the results array
const track = data.results[0];

// Step 5 — use the previewUrl to play the song
const audio = new Audio(track.previewUrl);
audio.play();

console.log(`Now playing: ${track.trackName} by ${track.artistName}`);

Try It — API Integration

This simulates the iTunes API response so you can see how the data looks and how to read it. The real version uses fetch().

Lines: 1Characters: 0
Output
Click "Run" in code control panel to see output ...

⏳ Asynchronous I/O

Full fetchAndPlay() — async/await + error handling

/**
 * Searches the iTunes API for a song and plays the preview.
 * @param {string} searchTerm - e.g. "every breath you take police"
 * @returns {Promise<void>}
 */
async fetchAndPlay(searchTerm) {
  try {
    const url = `https://itunes.apple.com/search?term=${encodeURIComponent(searchTerm)}&entity=song&limit=1`;
    const response = await fetch(url);          // async HTTP GET

    if (!response.ok) {
      throw new Error(`HTTP error: ${response.status}`);
    }

    const data = await response.json();          // parse JSON response
    const { trackName, artistName, previewUrl } = data.results[0];  // destructuring

    if (!previewUrl) throw new Error('No preview available');

    new Audio(previewUrl).play();
    console.log(`Playing: ${trackName} — ${artistName}`);

  } catch (error) {                              // API error handling
    console.error(`[iTunes] Fetch failed: ${error.message}`);
  }
}

fetchAndPlay('every breath you take police');
Same pattern as the leaderboard API — just a different endpoint. await pauses until the response arrives. Destructuring { trackName, artistName, previewUrl } pulls only what's needed.

Try It — Async/Await

Run this to see async/await with a simulated API call. The await pauses execution until the Promise resolves.

Lines: 1Characters: 0
Output
Click "Run" in code control panel to see output ...

📦 JSON Parsing

Connection to the Game

The game's PeppaMusic class uses this exact same pattern — it calls the iTunes API to fetch background music previews and plays them during gameplay. The same fetch()await response.json() → extract field → play flow powers both the leaderboard and the in-game music.

JSON Parsing — Extracting API Response Data

// Parsing the iTunes API JSON response
const data = await response.json();          // JSON.parse() equivalent

// Object destructuring — pull only the fields we need
const { trackName, artistName, previewUrl } = data.results[0];

// Leaderboard response parsing
const scores = await response.json();         // Array of score objects
scores.forEach(({ player, score, date }) => {
  console.log(`${player}: ${score} on ${date}`);  // destructuring + template literal
});
response.json() calls JSON.parse() automatically — it turns the raw text the API sends into a JavaScript object you can read. Destructuring pulls out only the fields you need.

Try It — JSON Parsing

Run this to see JSON.parse() and object destructuring. Try adding a new field to the JSON string and reading it.

Lines: 1Characters: 0
Output
Click "Run" in code control panel to see output ...

🎵 Live Demo — Play "Every Breath You Take"

Click the button to make a real iTunes API call and play the 30-second preview in your browser.

📚 Related Homework: JSON

JSON Homework - Demonstrates JSON parsing and serialization, directly applicable to API integration where data is sent and received as JSON objects.