Links:

Bricks and Tables

A nostalgic memory of a multiplication tables game from childhood inspired this project. I looked at the national archive for dutch educational games but could not find it. So I decided to recreate it for my son and use this opportunity to explore coding with Copilot. This project aims to build a web-based game from scratch, despite my limited experience with JavaScript and game development. The model I used is OpenAI’s GPT-4o.

link to game

My experience vibe coding with Copilot

generating the project structure

After giving the prompt for the game to Copilot, I was surprised by the amount of code it generated. It created a complete HTML file with a canvas element, a JavaScript file, and a CSS file. The generated code was quite impressive, and I was able to run the game without any modifications.

the project structure

the first result

The first result was a simple multiplication game where the player had to answer multiplication questions. The game was not functional, but after copy/pasting the errors from the javascript console it was fixed. but I wanted to add some visual elements to make it more engaging. I asked Copilot to generate a simple background and some bricks that would fall from the top of the screen. The generated code was a good starting point, but I had to make some adjustments to get the desired effect.

the first result

coloring the bricks

I had Microsoft copilot generate some assets for bricks. I wanted to have multiple levels with different color bricks, so I decided to recolor them. Github copilot was able to create a python script to generate multiple versions of the same image in slightly different colors.

from PIL import Image, ImageEnhance

# Load the red brick image
red_brick = Image.open("src/assets/brick_red.jpg")

# Define colors and filenames
colors = {
    "white": (255, 255, 255),
    "green": (0, 255, 0),
    "blue": (0, 0, 255),
    "purple": (128, 0, 128),
    "yellow": (255, 255, 0),
    "rainbow": None  # Special case for gradient
}

# Function to recolor the image
def recolor_brick(image, color):
    recolored = Image.new("RGB", image.size, color)
    return Image.blend(image, recolored, alpha=0.5)

# Generate recolored bricks
for color_name, rgb in colors.items():
    if color_name == "rainbow":
        # Create a rainbow gradient manually
        gradient = Image.new("RGB", red_brick.size)
        for x in range(gradient.width):
            r = int(255 * (x / gradient.width))
            g = int(255 * ((gradient.width - x) / gradient.width))
            b = 128
            for y in range(gradient.height):
                gradient.putpixel((x, y), (r, g, b))
        rainbow_brick = Image.blend(red_brick, gradient, alpha=0.5)
        rainbow_brick.save(f"src/assets/brick_rainbow.jpg")
    else:
        recolored_brick = recolor_brick(red_brick, rgb)
        recolored_brick.save(f"src/assets/brick_{color_name}.jpg")

I was quite impressed with this script, it was very quick and did exactly what I expected from it.

the recolored bricks

half bricks

I wanted the bricks to be staggered so that every other row had half bricks in the beginning. This proved a difficult task. This means that the rows that have half bricks should be shifted to the right. This also means it has one more brick per row than the others. This took a couple of tries to get right.

the half bricks are not drawn correctly

adding a character

I wanted a character to stand in front of the wall of bricks. I asked Copilot to generate a simple character of a craftsman. The issue was to generate multiple images of the same character with different facial expressions and holding different tools.

the character

the 'same' character with a sad expression

This was not going to work.

play testing

To view this video please enable JavaScript, and consider upgrading to a web browser that supports HTML5 video

When playtesting I found there were still some issues.

  • The focus was sometimes lost, resulting in keystrokes not being registered.
  • The game over event can be missed by continuing to play, and trying to hit enter on a result. this then closes the game over notification.
  • the game over does not clear the screen sometimes.

Still some bugs to solve together with copilot.

copilot confidence.

When writing the blog post I turned on autocomplete in the markdown file as well. One thing I noticed is that copilot can be very confident about itself. Often mentioning that it generated code that was working perfectly the first time or that I found it very impressive. It seems to be trained on a lot of examples of people praising it, or it might be gaslighting me into liking it.

copilot helping with hugo

I use hugo as a static content generator. I usually struggle with relative links and imports. I believe there might be some slight differences between templates. Copilot also did not help much here, pointing to non-existing files a couple of times. I ended up doing some trial and error to fix it.

fixing bugs with the rendering

Clearing the canvas and redrawing the bricks depends on asynchronous rendering. I believe the clearing of the canvas is not always done before the new bricks are drawn. However copilot was not able to help me with this. This issue will have to be resolved later. Maybe I can revisit this problem when I get a better model.