CS 18 (Spring 2026) Project 01: Images (Game)

In this project, you will implement images!

Code Correctness

If you haven’t already, please do the Setup for the course!

Goals and Deliverables

This week, you will render an image onto your screen!

Ticks and the Main Run Loop

We’re going to be working through a lot of graphics-related code in this class, so it’s important to understand the basic concepts behind drawing graphical content to the screen.

When we talk about graphics in the context of writing software, we are dealing with computing the contents of individual frames in real-time, clearing the canvas of the previous frame, and drawing the new computed frame, all at regular intervals. If you want your graphics to look realistic, you need to have a reasonable measurement of the time between frames, since our goal is to generate a graphical representation that is independent of the speed at which frames are processed by the computer. This is why low frame rates in graphics-intensive games result in a scene that is choppy – each frame encompasses a large amount of real-world time, causing objects to move large distances in each frame. The most basic implementation of a graphical application, then, looks as follows:

while app_is_running:
    dt = time_since_last_frame()
    object_pos = compute_new_positions(object_pos, dt)
    
    clear_screen()
    draw_frame_offscreen()
    display_frame()

We’ve provided a few helper functions to help facilitate writing these functions. You can see how they interface with the lower-level SDL2 library in sdl_wrapper.c.

Files of Importance

The above basic loop is implemented in emscripten.c, which relies on a number of functions you will write in your demos - namely, emscripten_init, emscripten_main, and emscripten_free. emscripten.c serves as the basic functionality of all our demos - you do not need to modify this file. Notably, because image.c implements the main while loop described above, your image.c file should not have a main loop.

The header file for the functions you write in your demo is state.h, which tells emscripten.c that these three functions are properly defined elsewhere. You do not need to write a state.c file - you should write the three functions in the header in your image.c demo as mentioned above. Notably, state.h also includes an opaque type definition for a “state”. This is a struct that you will define in each of your demos (here, in image.c). This state struct is how you will pass information between your three functions - anything you initialize in emscripten_init that you need in your emscripten_main loop should be put in this state accordingly and returned. You are responsible for defining the state’s parameters, malloc’ing it in emscripten_init and freeing it in emscripten_free.

Using the basic breakdown of the files above, and the documentation in sdl_wrapper.h and state.h, you should be able to get a good idea of how to implement a graphical application in C, using our wrapper library.

Although it does not use the same abstractions, the circle.c demo works with emscripten.c and state.h in the same way you will.

Images

The sdl_wrapper files contain functions that help us with rendering images. Take a look at sdl_wrapper.h for documentation on what the functions do. Your job will be to implement the functions themselves in sdl_wrapper.c. We’ll briefly go over what they do.

sdl_get_image_texture

This function initializes and returns a pointer to an SDL_Texture object that represents an image. It creates the texture with the help of IMG_LoadTexture, which takes in the filepath to the image and an SDL_Renderer *. Note that we’ll have to pass renderer to IMG_LoadTexture, which is the same SDL_Renderer * that is being used in our other sdl_wrapper functions like sdl_init, sdl_draw_body, etc. This was done intentionally and is an example of encapsulation: by defining all sdl-related helper functions in sdl_wrapper, we abstract away details of image rendering so that the user of the function only needs to worry about parameters like the correct filepath.

sdl_get_rect

This function initializes and returns a pointer to an SDL_Rect object. An SDL_Rect is an abstraction for a rectangle in the SDL library that has a location (x,y) and dimensions (w,h). Notice that in SDL, the origin (0,0) is at the upper left of the window, and the y coordinates increase from top to bottom, not bottom to top:

bounding boxes

sdl_render_image

This is the function that actually renders the image. SDL_RenderCopy will be helpful here. It takes in a SDL_Renderer * (notice a pattern?), SDL_Texture *, and two SDL_Rect *’s. The first SDL_Rect * represents the area of the texture to render, and the second SDL_Rect * represents the target area to render to. Both arguments can be set to NULL to represent the entire texture and rendering target, respectively.

image.c

In the image.c file, we’ve given you some constants that should be helpful when writing your code. Here are some other helpful reminders:

We want to load the SDL_Texture for our image only once. A common mistake when rendering images in C is loading a texture for the same image repeatedly in the game loop, unnecessarily allocating memory every time it runs. While not noticeable at this stage, once you start adding more images to your game, this may cause it to run much slower. In the worst case, this may lead to memory leaks!

Speaking of memory leaks, it’s important that any SDL objects that we initialize are disposed of properly in emscripten_free. Similarly to how structs that we define can have their own free functions (like list_free or body_free), SDL objects can also have their own free functions. In the case of SDL_Rect, we just malloc’ed it normally, so free works fine. However, we create a SDL_Texture using IMG_LoadTexture, which means that using free isn’t enough. The documentation should tell you which function to use to dispose of SDL_Texture objects properly.

If you are having trouble rendering images, SDL_GetError() may come in handy when debugging your code. For some help of how the functions work with each other, take a look at some resources our website!

Running and Testing your Code: make and the Makefile

All the projects in our class will have the following directory structure:

We have provided you with a Makefile which is set up to build the project. It allows you to run the following commands:

This year, we will be testing demos and games via a web interface hosted on labradoodle, using a library called emscripten.
Every student has been assigned a port, which you can see after running make game or make server, or by running cs3-port in a shell on labradoodle. To run your game, you should run make game or make server and then navigate to the link printed to the shell (the last line before “Serving HTTP on …”) and click the compiled demo .game.html file. If you see an error that says Address already in use, please run cs3-kill-web in the terminal and rerun make game. If you’ve made changes to your code but make game generates the link instantly, you might have to run make clean before make game again to recompile the game.

One additional part of testing any game/demo code will be ensuring that there are no memory leaks. For the physics engine, we can run the leak sanitizier after the test finishes, but for demos/games, there is no obvious “end of execution” unless the player loses/wins, which isn’t always defined. Thus, we’ve set up the “esc” key on the keyboard to run emscripten_free and kill the demo/game when pressed. Once you press “esc,” you should be able to see any memory leaks if you Inspect Element and go to “Console.” If you see any memory leaks, make sure to fix them!

If there are no memory leaks, the console should look like this (you can ignore the RuntimeError which will always get thrown when we kill the demo):

If there are memory leaks, the console should report the lines that are causing them like this (the relevant lines are boxed in blue):

In this example, the memory leak was caused by our malloc call on line 177 of pacman.c.

Note that there is nothing special about the “esc” key in this case; we’ve just chosen to bind it to emscripten_free. Once you start working on your final game, feel free to change this keybinding, but please don’t do so until we are done with all the projects this term.

We will take off points if your code has memory leaks, so be sure to check for them before you submit your code!

Feel free to play around with different images! As long as you meet the above guidelines, you are free to use different assets.

Once you are done, push your code to GitLab.

Once all your group members have finished, work with them to complete the extension (more information in the main project page).