Trace Runner gameplay demonstration.
Overview
Trace Runner is a 2D platformer video game built to display on an oscilloscope, combining modern game design with a callback to one of the first video games, Tennis for Two (1958), by William A. Higinbotham (Brookhaven National Laboratory). The project uses an Arduino Due to control game logic, a Nintendo Entertainment System (NES) controller to read player inputs, and a function generator to assist in output to the oscilloscope, which displays in XY-mode. Gameplay-wise, Trace Runner includes left and right movement, jumping, pits that send the player back to the start, and wall collisions between the player and terrain. Additionally, the game has custom music (handled by an Arduino Uno) and sound effects that play upon jump or death.
Technical Implementation
Display System
Oscilloscopes are typically used to measure voltages in a laboratory setting, displaying on a screen one or more voltage signals over time. This mode, of plotting voltage signals versus time, is referred to as "normal mode". For this game, an oscilloscope in a different mode, XY-mode, is used. This plots two voltage signals against each other, rather than over time. To create an image like the one seen below, a triangle wave of frequency 4 Hz is first input to the oscilloscope's X channel. This signal constantly scans across the entire screen, moving from left to right four times per second. To then display the game, player and terrain information are gathered from an Arduino Due (more details later), and input to the oscilloscope's Y channel with a frequency matched to that of the input triangle wave. This allows direct control of the visuals on screen from the Arduino without worrying about how to shift the waveform on the screen through phase or frequency changes.
Phase Runner displayed on a Tektronix 2 Series Mixed Signal Digital Oscilloscope in XY-mode (left) and normal mode (right).
Signal Processing
The game logic is primarily handled by an Arduino Due, a microcontroller (small computer) built to take in a variety of inputs, process them in code, and output other signals. For this game, the Arduino Due outputs the player and terrain signals as voltages through its two analogue out pins (DAC0 and DAC1). From here, the two outputs can be combined using an adder. This adder is built using an LF411 operational amplifier, as pictured below. The circuit takes a number of inputs, V1, V2, ... Vn, and outputs their sum, with the ability to scale each input through a chosen resistor R1, R2, ... Rn.

Schematic of adder circuit.
In order to display a stable image, the frequencies of the Arduino output signals and of the function generator must be exactly synced. As can be seen below, this is done by inputting a trigger signal from the function generator to the Arduino and instructing the board to wait for a trigger before beginning the drawing process.

Full build schematic for Trace Runner showing all connections.
The above image shows the complete build schematic for this project, beginning on the left with input from the NES controller (operation described later) to the Arduino Due. From here, the Arduino outputs the player and terrain voltages (determined by code) through its two DAC pins, which pass through the adder, then output to the oscilloscope's Y channel. In addition, two piezos (small buzzers that can play sounds) are used: a sound effects piezo controlled by the Due board, and a music piezo controlled by an Arduino Uno.
Game Logic
The Arduino Due handles all game logic including player movement, jump physics, terrain scrolling, and collision detection. Below is a simplified flow diagram of the code that handles game events.

Flow diagram of Arduino Due code.
In the diagram shown above, a terrain array is first initialized, as built by a spreadsheet and simple Python notebook, which can both be found in the GitHub repository for this project. This stores a number of horizontal and associated vertical terrain positions. The game initialises the terrain at location index 3, which forms the beginning of the level (and allows drawing of a wall at location index 0 to prevent the player from leaving the terrain on the left). Then, the code waits for a trigger event from the function generator. Upon trigger, the code both draws terrain and checks for player inputs.
To draw the terrain, the code begins at the current location index, then outputs the voltage associated with the next twenty array indices—once forward, and once backward, as previously discussed. The first 20 entries in the terrain array are shown below. As an example of terrain drawing, if the current location index is integer 8, the Due outputs the voltage associated with integer 40 (again, the exact voltage level is less important than the relative voltage levels). Then, while the location index remains at 8, the draw index moves to 9, outputting the voltage associated with 0. This continues to a modifiable "display_width" (set to twenty for this project) number of indices, before beginning the reverse draw, which operates similarly. On the reverse terrain sweep, the player is also drawn at the centre of the screen, as mentioned before.
int terrain[][2] = {{1,200},{2,40},{3,40},{4,40},{5,40},{6,40},{7,40},{8,40},{9,0},
{10,0},{11,0},{12,40},{13,40},{14,40},{15,40},{16,40},{17,0},{18,0},
{19,0},{20,0},...};First 20 entries of terrain array.
Controller Input
To handle player inputs through the NES controller, all that was needed was to connect the controller to the Arduino, which would then process inputs and adjust the game as necessary. To connect the controller, Joseph Corleto's article "NES Controller Interface with an Arduino UNO" was followed, which excellently describes both the internal operation of an NES controller and how to output its data to an Arduino.
Audio System
Sound effects are generated using a piezo buzzer controlled by PWM signals from the Arduino Due. Background music is handled by a separate Arduino Uno running custom music code, preventing timing interruptions between the microcontroller trying to play music while simultaneously attempting to draw the terrain. All the music and sound effects were custom written for this project, which posed a unique challenge! How can a melody and harmony be implied by a monophonic piezo buzzer line, while still sounding good? Inspirations here came from early Kirby music (see Jun Ishikawa's score to Kirby's Dream Land, 1992), as well as the Bach cello suites. The music is mostly diatonic, with occasional modulations up a whole step or to the relative major/minor. All music can be found both as audio and as score in the GitHub repository.
To convert the music to code that the Arduino could process and output to a piezo, ShivamJoker's "MIDI-to-Arduino" converter was used.
Results and Conclusions
This project successfully displays a playable 2D platformer video game on an oscilloscope. Several peers were eager to participate in playing this game, and multiple returned for a second try at the game after each had taken a turn. If desired, it would be fairly simple to add additional gameplay elements such as enemies that the player must avoid, or a "double-jump" ability through adjustment of the code and output on the player voltage line.
Aside from the addition of new gameplay elements, there are also several current game elements that could bear improvement, the most prevalent being the game's framerate. Currently, the input triangle wave has a frequency of 4 Hz. If the screen were updated more than four times per second, the play experience would be smoother, and the game would be more "responsive" to the player, understanding inputs as soon as a button is pushed. Increasing this framerate ultimately depends on the Arduino Due's ability to quickly cycle through code and the upper limit to how quickly the board can output analogue voltages. Given more time, this frequency could be found, and the framerate of the game would likely improve as a result. In addition, the Arduino Due has a fairly narrow range of output voltages, (0.55–2.75 V), limiting the height of available terrain to draw to the oscilloscope. By using the Due's 12-bit capabilities (instead of 8-bit) and amplifying the output signals, a wider range of controllable terrain would be achievable, rather than the current game state of mostly taking place in the lower half of the screen. Last, the primary difficulty new players mentioned was trouble identifying the "character" location on screen, since the player's colour is the same as the terrain. One extension of this project would be to draw the terrain on a much larger scale (for example, have 20 horizontal array values correspond to one "game unit"), which may give more flexibility in drawing an artistic shape for the character.
This project exists at the intersection of modern 2D platformer games such as Ori and the Will of the Wisps and Tennis for Two, building a call-back to the first video games while maintaining references and traditions that have built up in this space since then. Despite its simple gameplay, this game proved an enjoyable experience for lab peers, demonstrating the entertainment potential for electronics. Below is an image of the final setup.
Final setup for Trace Runner, showing controller, game console, oscilloscope, and function generator.
Code & Documentation
The complete source code, including Arduino sketches for game logic and music, is available on GitHub: