This research explores using physics to create computational music. In this project, we investigate the dynamics of a bouncing ball on a staircase and its application in generating musical notes using Mathematica. The objective of our research is to model the trajectory of a bouncing ball influenced by gravity and initial conditions, and to translate its kinetic behavior into musical expressions. Parameters such as the initial velocity of the ball, the number of stairs, and the number of balls are adjustable. As a result, the animation is capable of generating a variety of melodies.
​
The animation utilizes solutions to differential equations to simulate the ball’s motion, accounting for bounces and energy loss. The horizontal position of the ball (x-coordinate) determines the pitch of the generated musical notes, while the timing of the bounces dictates the rhythm. The result of our research is a three-dimensional animation of the bouncing ball displayed with a synchronized audio track playing.

Introduction

This project aims to create an animation of a bouncing ball that generates music based on its trajectory.
The process is divided into the following stages:
◼
  • Computing: calculating the ball’s trajectory and the points at which the ball bounces using differential equations
  • ◼
  • Animating: creating the visualization of the ball’s path using mathematical functions
  • ◼
  • Audio: generating audio that is synchronized with the animation
  • ◼
  • Expanding: adding controls, adjustable parameters and options; improving the movement of the ball
  • Modeling the ball’s trajectory

    To model the bouncing ball’s trajectory, we can use solutions to differential equations. Let
    (x(t),y(t),z(t))
    be the position of the ball given time
    t.
    At
    t0,
    let us set the ball to be at position
    (0,0.5,
    h
    i
    ),
    where
    h
    i
    is the initial height of the ball. For simplification, we only want the ball to have velocity in the x-direction initially, so we set
    ′
    y
    (0)
    ′
    z
    (0)0
    and
    ′
    x
    (0)
    v
    i
    ,
    where
    v
    i
    is the initial velocity of the ball. We also neglect all forces except the gravitational force, so we set
    ′′
    x
    (t)
    ′′
    y
    (t)0,
    and
    ′′
    z
    (t)-g,
    where
    g9.81m
    2
    s
    .
    ​For now, let us consider a static case: the ball is bouncing on a 4-step staircase with initial height
    h
    i
    5
    , an initial velocity of
    v
    i
    1,
    and a runtime of
    t
    max
    10
    seconds.
    In[]:=
    g=9.81;​​numSteps=4;​​heighti=5;​​velocityi=1;​​runtime=10;
    The function
    NDSolve
    solves for the functions in a differential equation given a set of equations. Before we can use the solver, we must first define a set of equations, as well as add a staircase and account for the bouncing motion:
    In[]:=
    {x[0]==0,​​y[0]==0.5,​​z[0]==heighti,​​x'[0]==velocityi,​​y'[0]==0,​​z'[0]==0,​​x''[t]==0,​​y''[t]==0,​​z''[t]==-g};

    Adding a staircase

    Let
    a(t)
    be a function that models a staircase. The function
    WhenEvent
    inside
    NDSolve
    is used to model the staircase. Whenever the x-position of the ball is an integer, we want to create a step until the height reaches 0.
    In[]:=
    WhenEvent[​​Mod[x[t],1]==0,​​If[​​a[t]>0,​​a[t]->a[t]-1,​​"RemoveEvent"]];

    Modeling the bouncing motion

    When the ball hits one of the stairs, we want the ball to bounce off with 90% of its previous velocity. To model this, we negate the
    z
    coordinate while multiplying the
    x,y,
    and
    z
    velocities by
    0.9
    Next, we want to store the points where the trajectory of the ball and the staircase intersect. The
    Sow
    function is used to obtain the coordinates of the bounces and the times of the bounces.
    The following code models the bouncing motion of the ball. The intersection points are stored in the form
    {x,y,z,t}
    :
    In[]:=
    WhenEvent[​​{z[t]==a[t]},​​{{x'[t],y'[t],z'[t]}->0.9{x'[t],y'[t],-z'[t]},​​Sow[{x[t],y[t],z[t],t}]}];

    Storing the position functions and contact points

    Combining all the previous code, we get the following:
    In[]:=
    {ballsteps,{points}}=NDSolve[​​{x[0]==0,​​y[0]==0.5,​​z[0]==heighti,​​x'[0]==velocityi,​​y'[0]==0,​​z'[0]==0,​​x''[t]==0,​​y''[t]==0,​​z''[t]==-g,​​a[0]==numSteps,​​WhenEvent[​​Mod[x[t],1]==0,​​If[​​a[t]>0,​​a[t]->a[t]-1,​​"RemoveEvent"]],​​WhenEvent[​​{z[t]==a[t]},​​{{x'[t],y'[t],z'[t]}->0.9{x'[t],y'[t],-z'[t]},​​Sow[{x[t],y[t],z[t],t}]}]},​​{x,y,z,a},​​{t,0,runtime},​​DiscreteVariables->{a}]//Reap;
    The list
    ballsteps
    stores the parametric functions
    x(t),y(t),z(t),
    and
    a(t)
    .
    In[]:=
    ballsteps
    Out[]=
    xInterpolatingFunction
    Domain: {{0.,10.}}
    Output: scalar
    ,yInterpolatingFunction
    Domain: {{0.,10.}}
    Output: scalar
    ,zInterpolatingFunction
    Domain: {{0.,10.}}
    Output: scalar
    ,aInterpolatingFunction
    Domain: {{0.,10.}}
    Output: scalar
    
    The list
    points
    stores the intersection points between the ball’s trajectory and the staircase and the times of intersection in the form
    {x,y,z,t}.
    In[]:=
    points
    Out[]=
    {{0.451524,0.5,4.,0.451524},{1.36397,0.5,3.,1.46536},{2.38116,0.5,2.,2.72114},{3.41626,0.5,1.,4.14104},{4.42637,0.5,0.,5.6806},{5.31493,0.5,0.,7.18538},{6.03466,0.5,0.,8.53968},{6.61764,0.5,0.,9.75856}}

    Generating an animation

    Making the staircase

    First, let us plot a 3-D staircase.
    Plot3D
    allows us to generate a plot of a three-dimensional function. The graph of the
    Floor
    function is convenient for replicating the staircase.
    In[]:=
    Plot3D[​​{Floor[numSteps+1-x]},{x,0,runtime*velocityi},{y,0,1},​​PlotRange->{{0,runtime*velocityi},{0,1},{0,heighti*1.1}},​​PlotStyle->White,​​Filling->Bottom,Exclusions->None,​​Axes->False,Mesh->True,MeshFunctions->(#3&),PlotPoints->ControlActive[20,40],MaxRecursion->ControlActive[2,4],Boxed->False​​]
    Out[]=

    Adding the ball

    Using
    Graphics3D,
    we create a point that is centered at the position of the ball. We can use
    ballsteps
    to get the position of the ball at any time
    t.
    In[]:=
    Graphics3D[​​{Blue,PointSize[0.025],Point[{​​ballsteps[[1,1,2]][t],​​ballsteps[[1,2,2]][t],​​ballsteps[[1,3,2]][t]+0.025}]}​​];
    We use
    Show
    to display both the staircase and ball simultaneously.
    Running the following code displays the animation at t=0:

    Animating the plot

    Playing the sound

    We run the following code to get the times between each bounce, in other words, the durations of the notes.
    Now we must get the pitches of the notes based on the x-coordinate of the ball. In Mathematica, 0 represents the middle-C pitch, and an integer n represents a pitch n semitones away from the middle-C. We store the x-coordinates into a list, then take the floor of each coordinate to get the pitches.
    Combining the audio and the animation, we get the following:

    Creating a DynamicModule

    Adding a start/reset button

    Start button:
    Reset button:

    Adding adjustable initial conditions and different instruments

    The following code allows the user to input the initial conditions of the ball:
    Image of the input interface:
    (Subsequent visualizations do not display the input field section of the interface, but do display the main output)

    Implementing multiple balls

    Generating different pitches

    In the above code, every ball generates the same pitches. We adjust the code to make each ball generate a different pitch to make the sounds more interesting.
    Adding this to our code, we get the following:

    Example animation with 2 balls

    Adding more interesting movement

    To make the movement of the balls more interesting, we can make them move side-to-side as shown below.
    The following code contains the final version of our animation, updated with more interesting ball movement:

    Conclusion & Future Directions

    We were able to successfully generate an animation of a ball that plays music based on its bounces. We were also able to expand our project to include adjustable conditions, instruments, and number of balls.
    In the future, I wish to expand my project so that it produces more interesting sounds. For example, I want to implement a feature where the balls collide in the air and produce additional sounds. This will increase the potential of the project to be able to generate more melodies. An idea that I have is to play the Super Mario Bros Theme with bouncing balls. Another idea that I have is that instead of balls, I could use cubes or tetrahedrons to play the music, with each face representing a different pitch. I also hope to include a feature where a user is able to import an audio file and the simulation will create a staircase with different-sized steps that replicates the melody. Finally, I hope to improve the interface of the animation with various colors and steps that light up when touched.

    Acknowledgements

    I would like to thank my mentor, Carlos Angulo, for providing support throughout the process of coding. I would also like to thank the Wolfram High School Summer Research Program for granting me the resources and opportunities to create this project.

    References

    Simulate a Bouncing Ball. (2024). Wolfram.com. https://reference.wolfram.com/language/example/SimulateABouncingBall.html‌

    CITE THIS NOTEBOOK

    Generating musical notes based on the trajectory of bouncing balls​
    by Ian Gu​
    Wolfram Community, STAFF PICKS, July 12, 2024
    ​https://community.wolfram.com/groups/-/m/t/3215610