It’s Particle Time! How To Use A Physics Engine With Framer

A module for adding 2D physics simulations to your Framer prototypes.

Giles Perry
Framer
8 min readApr 6, 2017

--

My first article on prototyping in Framer looked at how to write a custom layer class and save it as a module. This follow-up takes things to the next level with an experiment in physics.

What would it take to build a simple physics simulation in Framer?

I recently set myself a challenge to build a physics engine module for Framer. Something I could use to test out a game idea. To a non-developer like myself, that might seem ambitious. But when you take anything on in Framer most times you’ll find you can build on other people’s efforts. Plus there’s an awesome community on hand to help out when you get stuck. Now I want to share the results with you, together with a few tricks I’ve learned along the way.

TL;DR

Everything you need to use a physics engine in Framer is up on GitHub…

… and check out the demo on Framer Cloud. Enjoy!

https://framer.cloud/KXQHl/

Step 1 — Find a physics engine

I want to make a prototype where objects have mass, collide and bounce off each other. So I need a physics engine to handle the simulation.

Framer already has ‘powerful physics’ — scroll components, draggable layers and spring animations all rely on Framer’s built in physics engine — but there’s no way to access the engine directly. There has been chat about integrating a full blown physics engine in Framer. How’s that going? I have no idea…

Luckily, Justin Windle (a.k.a. soulwire) has written ‘A simple, lightweight physics engine written in CoffeeScript’:

Let’s download the ZIP file from Justin’s GitHub repo and see what’s inside.

There are three folders:

source’ contains Justin’s original CoffeeScript files. In ‘compiled’, the same code has been compiled to JavaScript. In ‘deploy’ the separate parts of the system are combined into a single file. I haven’t figured out how to import JavaScript modules in Framer so we’ll focus on the source code and see how well we get on.

Rename the source folder ‘coffeePhysics’ and drag it into the modules folder of a fresh Framer Project.

Step 2 — Modularise

The aim is to create a module so we can use the physics engine in any project that needs it. You can find out more about modules on Framer.com.

I want to change Justin’s code as little as possible. That way I’m less likely to break it.

Inside our new coffeePhysics folder is a set of files, each responsible for a separate aspect of the physics engine. For example, the math sub-folder contains the file vector.coffee which defines a class for creating and handling two dimensional vectors.

vector.coffee starts like this:

### 2D Vector ###class Vector   # rest of class definition goes here...

We don’t really need to understand the code. All we want to do is add the prefix exports so that the class is available to use in our Framer project:

### 2D Vector ###class exports.Vector   # rest of class definition goes here...

To include a module in a Framer project the code would normally be something like this:

# Include myModule.coffee in our prototypemodule = require "myModule"

But Vector.coffee is in a sub-folder of the modules folder, so we need to include its relative path:

# Include Vector.coffee in our prototypevectorModule = require "coffeePhysics/math/Vector"

Now that the module has been included in the project, we will be able to create an instance of the Vector class:

# Create a new Vector instancevectorA = new vectorModule.Vector

It would be nicer if we could write vectorA = new Vector. To do this we need to use a shortcut with the require statement:

# Include Vector.coffee 
# and assign the exported Vector class to a variable
{Vector} = require "coffeePhysics/math/Vector"

So far so good. We’ve figured out how to export a class from a module and include it in our project. But there’s an added complication…

Some classes in Coffee Physics don’t work in isolation. They build on another class, extending what it does. Take euler.coffee for example:

### Euler Integrator ###class Euler extends Integrator   # rest of class definition goes here...

The Euler class needs Integrator to be defined before it can extend it. Because the Integrator class is defined in a separate module, it’s not enough just to import both classes into our Framer project. Euler.coffee needs its own require statement:

### Import Integrator module ###
{Integrator} = require 'coffeePhysics/engine/integrator/Integrator'
### Euler Integrator ###
class exports.Euler extends Integrator
# rest of class definition goes here...

One module to rule them all…

Coffee Physics is made up of many files. We don’t want to import each file one by one every time we use it in a project. So we need to create a parent module that will do the job of tying everything together. This is relatively straightforward once you know how.

All the parent module has to do is import each of the required classes and then export them. We’ll name the parent module coffeePhysics.coffee and save it in the modules folder of our project. The code looks like this:

# Import the class...
{Integrator} = require ‘coffeePhysics/engine/integrator/Integrator’
# ...and then export it
exports.Integrator = Integrator
# and repeat for every other class…

The require statement needed in our project to include the entire engine is a bit more complicated than normal:

{Integrator, Euler, ImprovedEuler, Verlet, Particle, Physics, Vector, Spring, Behaviour, Attraction, Collision, ConstantForce} = require ‘coffeePhysics’

It works like this: {Integrator, Euler, etc.} is an object containing a set of variables, one for each class. require 'coffeePhysics' creates an object containing each exported class in the coffeePhysics module. The = operator assigns each class to the correct variable using the magic of destructuring assignment.

Our physics engine is complete! Let’s see if it works.

Step 3 — The moment of truth!

To test out our physics engine, I’ve taken the code example from the README in the Coffee Physics repo. Other than translating it from JavaScript to CoffeeScript, the only significant change is to use layers to draw the particles.

Rather than walk you through every line I’ll stick to the highlights. So download the project if you want to take a closer look…

To create a simulation, you need a Physics instance. This keeps track of time, and updates the position of particles according to the forces added to the system and the behaviours given to particles. You need to specify which numerical method the Physics instance will use to integrate Newton’s equations of motion.

# Create a physics instance which uses the Verlet integration method
physics = new Physics()
physics.integrator = new Verlet()

Adding a Collision instance allows the system to check when particles collide, and apply any resulting changes of direction and velocity.

# Allow particle collisions to make things interestingcollision = new Collision()

The next step is to add some forces. Our system will be governed by two forces: pullToCentre, an attraction force pulling everything to the centre of the screen, and avoid, which has negative strength and so pushes particles away from its centre.

# Design some behaviours for particles
avoid = new Attraction()
pullToCenter = new Attraction()
pullToCenter.target.x = Screen.width / 2
pullToCenter.target.y = Screen.height / 2
pullToCenter.strength = 120
avoid.setRadius( 100 )
avoid.strength = -1000

Now we’re ready to add some layers.

avoidLayer is a draggable layer constrained within the limits of the screen. You can flick and drag this layer around the prototype. We’re going to position the avoid force at the centre of this layer.

avoidLayer = new Layer
borderRadius: 100
size: 100
opacity: 0.30
avoidLayer.center()
avoidLayer.draggable.enabled = true
avoidLayer.draggable.constraints =
x: 0
y: 0
width: Screen.width
height: Screen.height

Next up, use a loop to create 200 particles.

Particles have mass, radius and position properties, with position defined using a vector. particle = new Particle( Utils.randomNumber(.1,1) ) creates a new particle with a random mass between 0.1 and 1. position = new Vector( Utils.randomNumber( 0, Screen.width ), Utils.randomNumber( 0, Screen.height ) ) defines a position vector at a random point on the screen. After setting the radius of the particle to be proportional to its mass, we move it into position:

# Create a particle
particle = new Particle( Utils.randomNumber(.1,1) )
position = new Vector( Utils.randomNumber( 0, Screen.width ), Utils.randomNumber( 0, Screen.height ) )particle.setRadius( particle.mass * 10 )
particle.moveTo( position )

Any behaviours effecting the particle are added to its particle.behaviours property. Then we add the particle to the collision.pool, so that collisions are handled, and to physics.particles, the array that keeps track of every particle in the simulation:

# Apply behaviours to the particle
particle.behaviours.push( avoid, pullToCenter, collision )
# Make it collidable
collision.pool.push( particle )

# Add it to the simulation
physics.particles.push( particle )

Coffee Physics doesn’t do the job of drawing our particles. We use Framer layers for that. Layer coordinates are measured from the layer’s top left corner, while a particle’s coordinates are from its centre, so that needs to be taken into account.

If we had more time, we could create a ParticleLayer class that extends the Layer class by adding particle properties to it. Instead, we’ll make the particle instance a property of the layer.

 # Create a layer to show the particle on the screen
ball = new Layer
x: particle.pos.x - particle.radius
y: particle.pos.y - particle.radius
size: particle.radius * 2
borderRadius: particle.radius
backgroundColor: colourCycler()

# Add the particle instance to the layer
ball.particle = particle

Finally, we can set it all in motion!

Every 60th of a second, reposition the avoid force to wherever the avoidLayer has moved to. Use physics.step() to update the simulation based on the elapsed time, and move the ball layers accordingly:

frameRate = 1 / 60# Set everything in motionUtils.interval frameRate, ->
# Update the position of the avoid target
avoid.target.x = avoidLayer.x + 50
avoid.target.y = avoidLayer.y + 50

# Step the simulation
physics.step()

# Update the position of the balls
for ball, i in balls
ball.x = ball.particle.pos.x - ball.particle.radius
ball.y = ball.particle.pos.y - ball.particle.radius

We’re done! Time to take a look at the finished prototype.

A few acknowledgements

Justin Windle is responsible for all of the clever stuff. Jonas Treub and Niels van Hoorn helped me piece it together. Thank you all.

Was this article helpful?

If you liked this article, please tap the little 💚 icon at the bottom of the page to help spread the word.

If you use my module to make something nice in Framer, or you have any thoughts, leave a comment 💬 to let me know. Thanks!

A bit about me

My name is Giles Perry. I’m a freelance UX Designer based in London, UK. I’ve been creating digital experiences for the last 17 years. Check out my website or find me on LinkedIn, and thank you for reading.

--

--