As the COVID-19 outbreak rages on, a good look at the outbreak would help. And when I saw a video by 3b1b on simulating epidemics, I tried to recreate what he did. So let's get started.
Since we need to put our graphics somewhere, we create a canvas in HTML and initialize it in JS.
<canvas id="c">
Your browser does not support the canvas.
</canvas>
And our JS:
var cv = document.getElementById("canvas")
var c = cv.getContext("2d")
cv.width = window.innerWidth
cv.height = window.innerHeight
First, we need variables such as the population and infection rate
(quick note, we’ll be coding this in JS, so make sure you understand basic JS before taking this tutorial).
var population = 100
var infected = 1
var speed = 10
var currentInfections = 1
Okay, so it’s pretty self-explanatory, but let's just go over it. The population variable is the amount of dots/people in the simulation. The infected variable is the start number of infections, and we have this because there can be 10 or 20 ‘patient zeros’ in an outbreak. The speed is how fast the dots move, and currentInfections is the number of infections. Now, the reason we have infected and currentInfections is because currentInfections is how many we have at any given time during the outbreak, while infected is how many we have to start with.
Next, we need an array where we will store each value, such as whether it is infected, susceptible, or recovered; the dots x and y; and its velocity, which I will explain in a moment.
Before I get to velocity, I want to explain what our model is. Our model is called a SIR model, which stands for susceptible, infected, and recovered. The susceptible population can get infected, the infected population can infect others, and the recovered population can no longer infect others, and, in this model, no longer can be re-infected.
Now, let’s get to velocity. This is the direction of the dots, such as left, right, up, or down. We will have two parts, velocity x, and velocity y. This way, dots don’t only go up, down, left, and right, but also in diagonals.
Because writing out all of this will take too long, we will use a for loop. We first define our array:
var dots = []
And now we will add to it:
for(var i = 0; i<population-infected;i++){
dots.push([Math.random()*Math.min(cv.width,cv.height)*3/4,Math.random()*Math.min(cv.width, cv.height) * 3/4,0,speed *Math.random(),speed * Math.random()]
}
Let’s go over it. Since each dot has several parts to it, we create sub-arrays inside it. The first 2 parameters are x and y. We place them in a random position on our canvas, but to place them in the center of the screen, we multiply them by 3/4.
Next, we have its state: susceptible, infected, or recovered. We can add more states like dead or immune instead of recovered, but let’s keep it simple for now. We put 0 as susceptible, 1 as infected, and 2 as recovered.
We have our velocity values next. We multiply the speed with a random number to get our velocity.
Now, you might have noticed that the for loop only covers 99 people, not 100. This is because we need to have a new for loop for the infected population.
for(var i = 0; i<infected;i++){
dots.push([Math.random()*Math.min(cv.width,cv.height)*3/4,Math.random()*Math.min(cv.width,cv.height)*3/4,1,speed*Math.random(),speed*Math.random()]
}
Now, we create a function called refresh() to do our animation.
var refresh = function () {
}
Before we get to the animation and drawing, make sure to draw the boundary of the “city”, which is a white rectangle.
ctx.fillStyle = "rgb(255,255,255)"
ctx.strokeRect(cv.width*1/4,cv.height*1/4,cv.width*3/4,cv.width*3/4)
Inside the function, we need to do our animation and drawing. We first draw circles for all the susceptible population, which will be blue, then the infected, which is red, and the removed/recovered population, which is grey. I’ll let you figure this one out.
Now, let’s animate them. We run a for loop which will go through the dots array and animates them.
for(var i = 0; i < population;i++){
dots[i][3]+=Math.random()*2-1
dots[i][4]+=Math.random()*2-1
if ( dots[i][3] >= speed ){dots[i][3] = 0}
if ( dots[i][3] <= -speed){dots[i][3] = 0}
if ( dots[i][4] >= speed ){dots[i][4] = 0}
if ( dots[i][4] <= -speed ){dots[i][4] = 0}
dots[i][0]+=dots[i][3]
dots[i][1]+=dots[i][4]
if(dots[i][0]>1*Math.min(cv.width,cv.height)*3/4){
dots[i][0]=1*Math.min(cv.width,cv.height)*3/4
}
if(dots[i][0]<0){
dots[i][0]=0
}
if(dots[i][1]>1*Math.min(cv.width,cv.height)*3/4){
dots[i][1]=1*Math.min(cv.width,cv.height)*3/4
}
if(dots[i][1]<0){
dots[i][1]=0
}
}
Now that we have that done, we need to start infecting others. To do this, we run a nested for loop which will find the infected dots. Once we find them, we will run a nested for loop to find other dots within the infection radius, which we put as 5. I’ll let you figure this one out too since it shouldn’t be too hard (HINT: there’s a double nested for loop).
Now, we’ve infected, we’ve drawn, and we’ve animated. We just need one more thing. Since people either die or recover, we should add that in. We add another element to the sub-arrays inside dots. At the beginning of the for loop (the first one) we put this:
dots[i][5]++
If you put a different variable in the for loop, replace i with that variable. At the end, put this:
if(dots[i][5] >= 200){dots[i][2] = 2}
This adds a “timer” to the infected ones and once it reaches 200, it changes to one of the removed dots.
We’ve now accomplished everything! To draw and completely animate, put this at the end of refresh():
window.requestAnimationFrame(refresh)
And then run the function:
refresh()
We're done!!
NOTE: This model does not represent the COVID-19 outbreak or any other outbreak, but it can be a very, very, very, very, very simple way to represent an outbreak.
What to Add
Several things you can add include:
- Dead and Recovered dots, instead of just the removed population as a whole
- Social distancing (this can be achieved by not moving some of the dots, and the idea was from a post by Harry Stevens)
- Quarantining a portion of the sick dots (only a portion, because not everyone who is sick shows symptoms and gets tested)
- Adding several communities where people travel from one to the other (idea from the 3b1b video I mentioned)
- Graphs, charts, data, etc.
- More customization in the parameters such as the infection rate, speed, recovery rate, etc
- A central spot, like a shop (also from the 3b1b video) For more complex modeling, I suggest you check out q9i’s article on disease modeling — Reopening Safely: The Data Science Approach on Medium (link at the end)
Examples
Here are some good examples
- Harry Stevens Washington Post Article
- q9i’s recreation of the 3b1b video
- My recreation of the 3b1b video
- Prajwal DSouza’s re-creation of the 3b1b video
- The 3b1b Video
Top comments (0)