Creating a Game with Desmos
Created: 2025/3/18

I created a simple obstacle avoidance game using Desmos! Let me show you how you can make a game like this.

Completed Game

Press the START/STOP button to start the game. Avoid all the purple obstacles to win!

Basic Mechanism

For details, it’s quicker to check the linked graph, but I’ll explain the basic mechanism here.

Updating the Graph with a Ticker

In Desmos, you can use a ticker to update variable values at regular intervals. For more details, check out this article.

Using Desmos: Actions and the Ticker

When creating a game, the main idea is to use the ticker to update the graph. Specifically, it updates the player’s coordinates and checks for collisions at regular intervals.

Player

The player’s coordinates and vertical velocity are managed using variables p,vyp,v_y. The variable rr represents the size (radius).

1
p=(1,3.5)p=(1,3.5)
2
vy=0v_y=0
3
r=0.5r=0.5
4
(xp.x)2+(yp.y)2r2(x-p.x)^2+(y-p.y)^2\leq r^2

The player is drawn as a circle to make collision detection easier later.

You can access the xx coordinate of pp using p.xp.x.

Obstacles

Obstacles are created as a list and are also drawn as circles.

1
Average distance between obstacles\textrm{Average distance between obstacles}
2
dball=16d_{ball}=16
3
Number of obstacles\textrm{Number of obstacles}
4
nball=20n_{ball}=20
5
Speed of obstacles\textrm{Speed of obstacles}
6
vball=0.02v_{ball}=0.02
7
Initial x coordinates of obstacles\textrm{Initial }x\textrm{ coordinates of obstacles}
8
Ox=dball[1,...,nball]O_{x}=d_{ball}\cdot[1,...,n_{ball}]
9
Radius of obstacles\textrm{Radius of obstacles}
10
R=0.3R=0.3
11
(x(Oxvballt))2+(y(R+3))2R2(x-(O_{x}-v_{ball}t))^{2}+(y-(R+3))^{2}\le R^{2}

This creates 2020 obstacles moving left at a speed of 0.020.02. To add variety, the positions and radii of the obstacles are randomized.

1
Initial x coordinates of obstacles\textrm{Initial }x\textrm{ coordinates of obstacles}
2
Ox=[5i for i=random(nball,n)]+dball[1,...,nball]O_{x}=[5i\ \mathrm{for}\ i=\mathrm{random}(n_{ball},n)]+d_{ball}\cdot[1,...,n_{ball}]
3
Radius of obstacles\textrm{Radius of obstacles}
4
R=[2i for i=random(nball,n)]+0.3R=[2i\ \mathrm{for}\ i=\mathrm{random}(n_{ball},n)]+0.3

Using random(a,b), you can generate a list of aa random numbers. The parameter bb is the seed value for random number generation, so changing bb produces different random numbers.

Updating Coordinates

The ticker executes the following actions to update coordinates:

1
T(d)=tt+dT(d)=t\to t+d
2
Py(d)={p.y+dvy3+r:p(p.x,p.y+dvy), p(p.x,3+r)}P_y(d)=\{p.y+dv_{y}\ge 3+r:p\to (p.x,p.y+dv_{y}),\ p\to (p.x,3+r)\}
3
Vy(d)={p.y>3+r:vyvydg,vy0}V_{y}(d)=\{p.y>3+r:v_{y}\to v_{y}-dg,v_{y}\to0\}

The argument dd is a special variable dt\mathrm{dt} available in the ticker, which represents the elapsed time since the last execution. For more details, refer to the article mentioned earlier.

Using Desmos: Actions and the Ticker

Here’s what each action does:

First, TT updates the time tt by adding the elapsed time.

PyP_y updates the player’s yy coordinate. Normally, it adds velocity ✕ elapsed time to the coordinate. However, if the player is about to go below the ground, it adjusts the coordinate to keep the player on the ground. 3+r3+r represents the yy coordinate of the ground plus the player’s radius, i.e., the yy coordinate when the player touches the ground.

VyV_y updates the player’s vertical velocity. Normally, it subtracts gravity acceleration ✕ elapsed time, but if the player is on the ground, it sets the velocity to 00.

Control with Buttons

Pressing a button makes the player jump. This is done by executing an action when clicking on the graph.

1
Initial jump velocity\textrm{Initial jump velocity}
2
jump=0.07j_{ump}=0.07
3
Jump=p(p.x,p.y+0.01),vyjumpJ_{ump}=p\to(p.x,p.y+0.01),v_{y}\to j_{ump}

The action slightly adjusts the player’s position upward to ensure it’s considered off the ground.

Collision and Clear Detection

Detecting collisions with obstacles is crucial. This is done by calculating the distance to obstacles.

1
Collision={min((Oxvballtp.x)2+(R(p.y3))2(R+r)2)1:over1}C_{ollision}=\left\{ \mathrm{min}\left(\dfrac{(O_{x}-v_{ball}t-p.x)^{2}+(R-(p.y-3))^{2}}{(R+r)^{2}}\right)\le1:o_{ver}\to1\right\}

Although it looks complex, the logic is simple. The numerator (Oxvballtp.x)2+(R(p.y3))2(O_{x}-v_{ball}t-p.x)^{2}+(R-(p.y-3))^{2} represents the squared distance between the centers of the obstacle and the player. The denominator (R+r)2(R+r)^{2} is the squared sum of their radii. If this fraction is less than 11, it means the distance between their centers is less than the sum of their radii, indicating a collision.

Since there are multiple obstacles, this fraction is a list. If the minimum value of this list is less than 11, it means at least one obstacle has collided with the player.

This collision detection is executed by the ticker, checking for collisions during each update.

Clear detection works in a similar way.

1
Clear={max(Ox[Ox.count]vballt+3,over)0:clear1}C_{lear}=\left\{\mathrm{max}(O_{x}[O_{x}.\mathrm{count}]-v_{ball}t+3,o_{ver})\le0:c_{lear}\to1\right\}

Ox[Ox.count]O_{x}[O_{x}.\mathrm{count}] is the last element of OxO_x, i.e., the initial xx coordinate of the last obstacle. Ox[Ox.count]vballt+3O_{x}[O_{x}.\mathrm{count}]-v_{ball}t+3 represents the xx coordinate slightly to the right of the last obstacle. If this becomes less than or equal to 00, the game is cleared. Of course, it also checks that the game is not in a game-over state.

Retry Button

Pressing the retry button resets various variables.

Notably, nn+1n\to n+1 is used. nn is the seed value for generating random obstacles. Changing this value during a retry alters the size and position of the obstacles.

Tips

Responsive Graphs

A common challenge when creating Desmos graphs is that they may break when viewed on devices with different screen sizes or when zoomed in or out.

For example, creating a graph on a large tablet screen might look like this:

However, opening the same graph on a smartphone might look like this:

The "BOX" label appears larger, breaking the layout.

This happens because the label size is adjusted to remain readable based on the screen size.

To prevent such layout issues and create responsive graphs, there are two key points:

Key Points for Responsive Graphs

  • Specify label (point) sizes using width\mathrm{width}
  • Fix the graph's display range

Desmos provides a special variable width\mathrm{width} representing the screen size. By using this to specify label sizes, the relative size of labels remains consistent regardless of screen size.

For example:

1
s=0.002widths=0.002\mathrm{width}

Using this ss to specify label sizes ensures they fit neatly:

Fixing the display range is also important. Since label sizes remain unchanged when zooming in or out, fixing the display range prevents layout issues caused by zooming.

Optimizing Graph Performance

In this game, there are many obstacles, which can make the graph heavy. To optimize performance, only obstacles visible on the screen are drawn and checked for collisions.

Specifically:

1
m=floor(vballtdball)m=\mathrm{floor}\left(\dfrac{v_{ball}t}{d_{ball}}\right)
2
(x(Ox[m,...,m+2]vballt))2+(y(R[m,...,m+2]+3))2R[m,...,m+2]2(x-(O_{x}[m,...,m+2]-v_{ball}t))^{2}+(y-(R[m,...,m+2]+3))^{2}\le R[m,...,m+2]^{2}

This ensures that only obstacles potentially visible on the screen are drawn.

SHARE
Images are created with the Desmos Graphing Calculator, used with permission from Desmos Studio PBC.
© 2024 UNS Physics. All Rights Reserved.