Physics Demos!

Making physics demos can be fun! Let's learn how to do this. You may already be familliar with much of the content of this document. If so, feel free to skip or skim large portions of it!

If you find yourself confused, this document can probably be improved! Ask questions, suggest improvements, or point out a confusing part of the document here. If interested in contributing, please fork the project and submit a pull request! Thank you for reading!

\gdef{\Arctan}{\textsf{Arctan}}

What is HTML?

Hypertext Markup Language (HTML) is a powerful tool and is used by practically every site on the internet. Because of its ubiquity and power, we will be building our demos with it!

Tags in HTML

HTML uses tags to format text. For example, <b>this text is in bold,</b> but the following text is not.

Tags tell browsers what to do with the text inside of them. <tag_name_here> starts a tag named "tag_name_here" and </tag_name_here> ends the tag! Let's see some examples!

Try using them in the editor below!

Notice the text surrounded by <!-- and --> above, when in the Editor tab. That text is in a comment! It will not be displayed as a part of the website.

Setting up the Document

<!DOCTYPE html> starts our documents — it tells the web browser that we are, in fact, using HTML (instead of something like XHTML).

Even though we tell web browsers that we intend to use HTML by starting our documents with <!DOCTYPE html>, we can put things that are not HTML in an HTML document (like JavaScript). As such, we need the <html> </html> tag.

The <html> </html> tag surrounds the contents of the document -- it surrounds all of our HTML. The <body> </body> tag is used to mark the website's content.

The <head> </head> tag surrounds information related to parts of the website outside the page. For example, <title> sets the page's title to this text! </title>

Browsers use the <title> </title> to show people using the website which page is which by, for example, putting this text at the top of the screen, in a tab label.

This is an example:

More HTML Tags

Before continuing, let's look at several additional elements!

Introduction to JavaScript

We will be using JavaScript to make our simulations act as they should. To run JavaScript, we can put it inside a <script></script> tag!

Some Quick Information JavaScript Comments!

Read about them in the comments below!

Changing Text!

To change text, we need to be able to let JavaScript know what we want to change. To do this, we give JavaScript a selector. This might be the name of a tag (like the "p" in a <p> </p>).

Things we can change are part of the HTML document. To query and use them, we give JavaScript a selector. We can specify it like this: document.querySelector("p") queries a <p> </p> from the document.

Notice that we put "p" in quotation marks in the selector -- this tells JavaScript that "p" isn't the name of a variable, it represents the text, p.

We write let aParagraph = document.querySelector("p") to get the first <p> </p> paragraph and store it in the new variable aParagraph.

More generally, document.querySelector("p") gets the paragraph and let aParagraph = makes the new variable, aParagraph, and sets it.

Notice that we set the textContent of the paragraph just like we set a variable! We can use textContent just like other variables!

Text in JavaScript

Put text in quotation marks to mark text as text! If we don't do this, JavaScript assumes we are are refering to a variable.

For example, to set the text of aParagraph to the text "some text", we can do this:

We can join two strings together with the + operator. For example, "some text" + " joined with even more" is equivalent to "some text joined with even more".

From here, I will call HTML tags and their contents "elements" — "tag" is specific to the text between <these brackets>.

As such, we can add additional text to an element by setting adding text to its textContent. For example, aParagraph.textContent = aParagraph.textContent + " with new text"; adds the text " with new text" to the end of the contents of aParagraph.

Adding text on to the end of existing text (appending it) is so common that JavaScript lets us write aParagraph.textContent += " with new text".

This shorthand is equivalent to writing out aParagraph.textContent = aParagraph.textContent + " with new text", but it is easier to read and type.

Let's see an example!

Changing HTML with JavaScript

Although we can use element.textContent to change the text of an element, we can't use it to make new elements.

Let's see what happens when we try!

We can, instead, change element.innerHTML!

What?! Only part of the text was italicized! Only the text we added with aParagraph.innerHTML = "<i>Will this text " was put in italics!

This is because each time we set element.innerHTML, the browser expects the new innerHTML to only contain complete elements. It updates the page just after you set the HTML (but it might take some time for you to be able to see it).

We can do it like this instead:

f\left(\textbf{unctions}\right) in JavaScript!

In mathematics, we use functions to organize expressions. In JavaScript, we do the same!

Let's start by translating the function, f(t) = -9.81 \frac{m}{s^2} \cdot t^2, into JavaScript. See below!

let f = (t) => -9.81 * t * t is one of several ways to make a function! We can evaluate f(t) at some t just by typing something like f(12). f(12) evaluates f at t = 12 s.

How else can we do this?

While let f = (t) => -9.81 * t * t-style notation is useful, it only lets us evaluate a single line of code! If we want f to do more than evaluate a single expression, we can surround its contents with curly-braces. For example,

let f = (t) =>
{
    let displacementFromAcceleration = -9.81 * t * t;
    return displacementFromAcceleration;
};
evaluates f with two sub-expressions — it acts like let f = (t) => -9.81 * t * t, but allows us to divide f into multiple expressions!

If we didn't use a function to represent f, our program might look like the below:

By moving -9.81 * t * t into a function made our code much easier to read!

Note: Writing f(something) is called “calling f”. For example, if I wrote doSomething(32), I would be calling doSomething with 32 as an argument. I wonder why this is the convention...

Repeating Things!

JavaScript supports running code in loops! There are several ways to write loops. Let's look at two of them!

while Loops

while (true) { repeats code } between the “{”, “}” braces while true is true.

Of course, true is always true, so this loop repeats the code inside the { } braces until we stop running the program. More useful would be to replace true with something else...

Similarly, we can write: while (false) { x = 7; }. false is not true, so x is never set to 7.

Let's see another type of loop! Above, we wrote output += "<p>" + f(8) + " m</p>"; many more times than we needed to! Let's see how to simplify this!


for Loops

// A for loop
for (let x = 0; // Start with x = 0.
        x < 10; // Loop while x < 10.
         x++)   // Increase x by one each time we loop!
{
    // Do something with x.
}

This is equivalent to:

// A while loop
let x = 0;

while (x < 10)
{
    // Do something with x.
    x++; // This line is very easy to forget!
    // x++ is a more concise way of writing x = x + 1.
}

Let's see how this can simplify our earlier example.

\MaTh in JavaScript!

Like a calculator, JavaScript supports arithmetic! Let's see some examples:

Mathematical Constants

Several mathemtaical constants can be accessed in JavaScript through the Math variable. Like document, we can write Math.something to get some entity associated with Math.

Trigonometry

Trigonometric functions accept radians in JavaScript and can be accessed via Math.functionNameHere(amount). For example,

Other Useful Functions

Operations With Booleans

Named after the mathematician, George Bool[ Better Citation Needed 🙂 ], Booleans are values that are either true or false.

Booleans are represented by true and false in JavaScript. This type of variable is useful for determining which pieces of code should run when.

// Say we have defined,
let T = true;
let F = false;

Practice!

Try these concepts out in the editor below!

let F = false;
let T = true;
F == false
true == false
F != true
Math.sin(Math.PI)

What is the result of running F == 0? What if happens if you run F === 0? What about true == 1? What is the difference between == and ===?

CSS — Cascading Style Sheets

CSS lets us change how parts of our demo will look.

Just as JavaScript is run when within a <script> </script> block, we write CSS within a <style> </style> block. Let's see an example!

Okay! Now, let's just make one of the paragraphs red by using an id!

Notice that we used document.querySelector to access the paragraph by its ID in a way similar to that done in CSS: #styleThisOne.


Filling the Screen!

Now, let's see how to fill the screen with an element. Say, a <div></div>.

We'll want to set its width and height:

<style>
div
{
    width: 100vw; /* 100% of the screen's width (vw). */
    height: 100vh; /* 100% of the screen's height (vh). */
}
</style>
                

To see it, let's give it a border.

<style>
div
{
    width: 100vw;
    height: 100vh;

    border: 1px solid black;
    /*
            ^     ^       ^
            .     |_____  |
        The border is   | . The border will be black!
        1-pixel thick.  .
                It will be a solid
                line (as opposed to
                dashed or dotted).
    */
}
</style>
                

All together,

Oh no! The <div></div> is big enough, but it isn't at the top-left of the screen. Let's remove the extra space:

Notice that we selected both body and html with body,html and used this to set their margin to zero.

We still, however, have a scrollbar. This is because, currently, the border adds padding to the outside of the <div>. Let's fix that:

It works! By adding box-sizing: border-box, we moved the moved the div's border inside the box div.


A Fun Example!

You can skip this. The below example demonstrates some of the fun things that can be done with just CSS!

Drawing!

We can draw on a <canvas></canvas> element with JavaScript. Let's see how.

Setting it Up

Like other elements, we can access a canvas element using document.querySelector. Unlike other elements, we will be using getContext to get a variable we can use to draw on the canvas. Here's an example:

As we see above, the canvas, by default, does not fill the entire screen. As we saw earlier, we can resize elements. Let's make the canvas' size fit that of the screen!

Changing the Number of Pixels

At present, as the screen shrinks and grows, our square is stretched into a rectangle!

We want to make the drawing surface the same size as the <canvas> </canvas>.

We can use the following to get a canvas' display size and its drawing surface size:

Setting canvas.width and canvas.height set the size of the drawing surface!

To make the drawing surface fit the canvas, we can do this:

canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;

Let's see an example:


Drawing with a ctx

To draw shapes on a canvas, in general,

  1. We start a path. This can be done through ctx.beginPath().
  2. We add onto the path. This can be done through ctx.lineTo(x, y), ctx.rect(x, y, width, height), and more!
  3. We do something with the path. To fill it, we can use ctx.fill(), or, we can trace it with ctx.stroke().
For example,
let ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(50, 50);
ctx.lineTo(50, 0);
ctx.stroke();
            

Notice that, above, we also included code to set drawing settings (e.g. rotation, scaling, translation), and ctx.restore them!

For a full tutorial on the HTML5 Canvas, try this tutorial on MDN. If the link is broken, search for the MDN HTML5 Canvas Tutorial.


Animations!

If we use a while loop to do this, the browser will wait indefinitely for our script to stop running before showing any changes we've made to the canvas.

Instead, we use requestAnimationFrame to tell the browser to call a function after it has had time to update the contents of the page.

let f = () => // f is a function of nothing.
{
    // Move things.
    ctx.translate(1, 1);

    // Draw things.
    ctx.fillRect(0, 0, 100, 100);

    // Draw again!
    requestAnimationFrame(f);
};

requestAnimationFrame(f);
            

Note that we can get the current time in milliseconds (relative to some time years in the past) with (new Date()).getTime().

(new Date()) accesses the current time. getTime gives us this time in milliseconds.

Using this, we can make something like this:

Notice that, above, we only update canvas.width and canvas.height if the drawing surface and the canvas' sizes are different. This is because setting canvas.width and canvas.height has side-effects, most notably, clearing the screen (I find it makes much more sense to clear the screen using ctx.clearRect).

Note that we clear the canvas with ctx.clearRect. Notice how similar this is to our use of ctx.fillRect and ctx.strokeRect!

Creating a Wave-Superposition Demo!

Let's make a physics demo that demonstrates wave superposition!

This example will walk you through the process of creating and sharing a demo! We will be using GitHub for hosting. If you don't have a GitHub account, please create one now.

Setting up GitHub Hosting

  1. Create a new GitHub repository by clicking the “new” button (on GitHub).
    • This can be either public or private — it can be changed later under the “Settings” tab of your project. Note: This has only been tested with a public repository.
  2. Select Add file, then Create new file.
  3. Name the file tutorial.html.
  4. As demonstrated below (and above, and in almost all examples provided in this document), set up the HTML document.
    • Your document should now look somewhat like this:
  5. Scroll down. Making sure commit directly ... is selected, click .
  6. Select the settings tab (it may be hidden within the overflow menu, ).
  7. Scroll down to ...

    GitHub Pages

    Some text telling you to select a branch and save will be here.

    ...
  8. Select the main branch of your project.
  9. Click .
  10. Select the Code tab at the top of the screen.
  11. Click on the github-pages link under Environments (open this in a new tab).
  12. Click .
  13. You should see a 404 Error page! By default, requests for https://your-username.github.io/your-repository-name/ are re-directed to https://your-username.github.io/your-repository-name/index.html. We haven't made that page yet.
  14. On the main page for your project (the page you used to add tutorial.html), select Add File, then New File.
  15. Name the file index.html so that https://your-username.github.io/your-repository-name/index.html displays a page.
  16. Set its contents to something similar to this (note the link included to tutorial.html!):
  17. this new file directly to the main branch of your repository.
  18. Click github-pages under environments and test the link!
  19. Back on the main page of your project (where you created index.html and tutorial.html), click tutorial.html, then click the edit () button.
    • This is where we will be creating the tutorial!

Making the Demo

To start, let's replace the contents of <body>...</body> in tutorial.html with a canvas and make it fill the screen.

We'll probably want the waves to move, so we'll add animation as we did above — with requestAnimationFrame and a function.

This is very similar to what we did before! The biggest difference is the drawWaves function.

It seems easiest to graph waves with x and y in a fixed range. Say, from negative one to one.

To do this, we'll want to change our drawing settings. To keep our old settings (which draw things normally, without scaling, rotating, etc.), we use ctx.save().

We restore the saved settings with ctx.restore(). We've seen this before!

To do this transformation, we first want to center everything — we translate by (canvas.width / 2, canvas.height / 2). This causes an object that would be drawn at (0, 0) to be drawn at (\frac{1}{2} \cdot \texttt{canvas.width}, \frac{1}{2} \cdot \texttt{canvas.height}).

Now, (0, 0) is at the center of the screen! Let's make (1, 1) be at the top-right of the screen!

How, then should we scale it? Here are some of our options:

Looking at these, we can see:

ctx.scale(canvas.width / 2, canvas.height / 2) might seem like it should work. Unfortunately, this causes (1, 1) to be in the bottom-right corner of the screen.

This is because, without scaling/translation, (0, 0) is at the top-left corner of an HTML5 canvas!

While this doesn't match our conventional placement of the origin when graphing functions, it does match how we write English — starting at the top-left of a page and moving to the right and sometimes down.

To make (1, 1) be at the top-right of the display, we need to reflect over the y-axis! In other words, we need to ctx.scale(1, -1).

ctx.scale(canvas.width / 2, -canvas.height / 2) both stretches and reflects — it maps (1, 1) to the top-right corner of the screen, as desired.

// To summarize,
ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.scale(canvas.width / 2, -canvas.height / 2);

// moves the origin (0, 0) to the center of the canvas
// and treats the distance from the origin to the top
// of the canvas be 1 (and to the right of the screen
// and to the left, ...).

To summarize, we first ctx.translate by (canvas.width / 2, canvas.height / 2) to move (0, 0) to the center of the screen.

We then ctx.scale(canvas.width / 2, -canvas.height / 2.

Don't forget to your changes! After doing this, re-open the file and start editing again.

Drawing the Waves

Let's start drawing!


As a quick review:


Debugging Tip! Most browsers can provide a summary of detected errors in JavaScript code. Often, this list can be accessed by pressing the F12 key, then clicking on the Console tab in the window that appears.

Why is the line so thick?

By scaling the canvas, we changed the size of the line!

We can fix this by moving ctx.stroke() to just after we restore the canvas' settings. The width of the line is relative to the (transformed) size of the canvas. As such, restoring before drawing makes the line a more-reasonable width.

Okay. The demo works. Somewhat.

We probably want to choose different functions. While the sine function is a good model for a wave, pulses do a better job of demonstrating the principle of superposition.

Let's change our function!

One option is to make f(t) have a graph similar to that of a normal distribution:

f(t) = e^{-x^2}

f(t)'s graph then looks like this:

Graphing f as a function of a sine wave (so that we have repeated pulses)...

Making it Better!

Now that it works, let's make it better!

Let's add a checkbox that hides/shows f(t2)!

We can create a checkbox using the <input type="checkbox"></input> HTML element, which can also be written as <input type="checkbox"/> because we won't be putting anything inside of it.

After we store the input in a variable, say checkbox, we can determine whether it is checked by accessing checkbox.checked!

At present, this article is unfinished! This part of the article has yet to be written!

Embedding a Demo in a Discussion

For course discussion services like Canvas, to embed a demo in a discussion, you'll want a link to the demo.

One option is to use GitHub. See GitHub's documentation for more information.

After you have a link to the demo, create an <iframe></iframe> element in your discussion post. Many platforms include a “edit as HTML” button that allows you to do this.

Set its src attribute to the URL for your demo.

For example,

In the below editor, you might click and add <iframe src="https://your-username.github.io/your-repository-name/tutorial.html"> </iframe> to your post.

Some information here.

Add the demo here?

Above, notice that I added style="box-sizing: border-box; width: 100%; height: 500px;"

Although generally considered bad programming style, setting styles through attributes can be done.

Here, it is especially useful because many discussion-post editors do not allow <style></style> blocks! As such, we need to make any stylistic changes in-line!

The default size of an <iframe src="..."></iframe> is rather small:

As such, we set the iframe's width to as large as its parent (100%), and height to a fixed amount (500 px). <iframe>s are often given borders, so we also set its box-sizing to border-box to prevent the iframe's box from being larger than its container!

Our resized iframe looks more like this:

This iframe should be large enough to fit our demo!

You caught my mistake! I should be writing “evaluates to” or “is equivalent to” here instead of “is”. While the expression !false (read “not false”) is equivalent to true, it is not the same expression.

When I say “equivalent to”, I mean “approximately equivalent to”. This is because of how JavaScript stores numbers. Please read this article.

When we run JavaScript, we run it in the context of a website. Closing the current tab or the entire browser causes our program to stop running.

Say we have the following code:

If we instead wrote while (true), output grows until it causes our script to stop running early! There is a maximum variable size!

Let's see what this is through some examples!