Skip to main content

· 3 min read
Physion

Physion is now equipped with audio support, thanks to the integration of the Tone.js library. In this blog post, we'll briefly explain what Tone.js is and how you can use it to enhance your Physion projects with audio.

While most of Physion's features are easily accessible through a user-friendly graphical interface, audio support, at least for now, is accessible exclusively through scripting. This means that you won't find a convenient UI tool for adding an "audio component" to your scenes. Instead, you'll need to import the audio library into your scripts and use it to create various sounds and audio effects. Let's dive into the process.

Tone.js

Before we explore how to use Tone.js in your scripts, let's briefly introduce the library.

Tone.js is a JavaScript library designed for audio and music creation on the web. It offers a framework for synthesizing and processing sound, making it easier for developers to build interactive music and audio applications directly in the browser.

Basic usage

Let's consider a very basic example of how you can utilize Tone.js in your scripts.

In the following very basic example, we'll create a NodeScript responsible for playing a sound whenever another object collides with it.

class BlipPlayer {

constructor(node) {
this.node = node;
this.initialized = false;
}

async initialize() {
const Tone = await physion.utils.importTone();
this.synth = new Tone.PolySynth(Tone.Synth).toDestination();
}

update(delta) {
if (!this.initialized) {
this.initialize();
this.initialized = true;
}
}

onBeginContact(bodyNode, contact) {
if (this.synth && contact.IsTouching()) {
this.synth.triggerAttackRelease("C4", "32n");
}
}
}

Let's break down what this NodeScript does:

As with all NodeScripts, the constructor is called with one argument: the Node that the script is going to be attached to. It's the responsibility of the script to store a reference to this Node so that a later time it can reference it. Also, in the constructor we're initializing a data member named initialized to false to denote that the script hasn't yet initialized.

As the name suggests, the initialize method is responsible for initializing the NodeScript. In this particular case, we need to import the Tone.js library and when this is done then create our synth object. Later on we'll use the synth object to play our sounds.

In the update method, we simply check if we have already initialized and if not then we call our initialize method.

Finally, in the onBeginContact method we check if the contact is touching and if we have already created our synth and if both of these conditions are true then we go ahead and play a note via our synth object. In this example we're playing the C4 note but any other note could be used instead.

The above example serves as a foundation for understanding how Tone.js can be integrated into your projects, allowing you to create diverse audio experiences within your scenes. As you explore audio support in Physion further, you'll discover many more possibilities for enhancing your creations with audio.

In Tone.js page you can find many more examples on how this library can be used in order to produce various sound effects.

Stay tuned for more in-depth tutorials and creative use cases for audio support in Physion with Tone.js!

· 2 min read
Physion

A feature that up until now was missing from Physion was the ability to create graphs. With graphs we can easily interpret data and better understand how properties of objects participating in our simulation change over time.

Graph Tool

You can create graphs using the graph tool. Once the tool is selected, you can click on any body in your scene to create a graph for. After you select a body using the graph tool, a new panel will appear in your Scene:

The header of this panel contains the title of the graph (e.g. "Linear Velocity") and two buttons on its right side. This panel will contain the graph of the selected body. By defult the Linear Velocity vs Time will be plotted.

At the top right of the graph panel you can find two buttons; the select button will select the graph and make it available for changes in the property editor and the close button will remove the graph from the Scene.

GraphNode

The underlying node for graphs is the GraphNode. As with all other types of nodes, you can edit its properties via the Property Editor. These properties are the following:

PropertyDescriptionDefault Value
bodyNodeIdThe id of the BodyNode of the graph. Data for the graph is acquired from this body""
propertyThe property of the BodyNode to be used in the graph. Note tha currently only the following properties are available: x position, y position, linear velocity x, linear velocity y, linear velocity, angular velocitylinearVelocity
paneXThe x position of the graph pane (in pixels)0
paneYThe y position of the graph pane (in pixels)0
paneWidthThe width of the graph pane (in pixels)400
paneHeightThe height of the graph pane (in pixels)300
backgroundColorThe background color of the graph pane0x0 (black)
backgroundAlphaThe background alpha of the graph pane0.5
lineColorThe line color used in the graph0xff0000 (red)
showGridWhether to show grid lines for the graphtrue

· 4 min read
Physion

Up until now, the physics engine used by Physion was Box2D. Box2D is an open source 2D physics engine written in C++ and it is used in many games and apps.

Although Box2D is a mature physics engine with many features, it doesn't "natively" support fluid simulation. After some research on the web, I came across LiquidFun. As mentioned in LiquidFun's website:

LiquidFun is an extension of Box2D. It adds a particle based fluid and soft body simulation to the rigid body functionality of Box2D.

As it turns out, transitioning from Box2D to LiquidFun is quite easy. All the features supported in Box2D are also supported in LiquidFun so there's no need for any major code changes when switching to LiquidFun.

Although LiquidFun supports a rich set of features, we will start simple; the transition to LiquidFun will have one simple goal: Allow the user to convert a rigid body into a liquid or an elastic (soft) body.

When a rigid body is "liquified" or "elastified" it will be removed from the Scene and in its place a new node will be added. This new node will contain all the particles representing the liquified or elastified body.

Liquify / Elastify Tools

The first change that was implemented was to add two new tools in the toolbars.

These two new tools are:

  • Liquify Tool
  • Elastify Tool

The user can select any of these tools and then click on a rigid body in the Scene in order to liquify or elastify it.

Some notes:

  • The visual feedback of these new tools is minimal (just a single sprite)
  • These tools will only allow liquifying/elastifying a single body at a time (i.e. single click selection)

Liquify / Elastify Commands

Both of these new commands will work in a very similar manner:

  • They will create a new LiquidNode or ElasticNode using the selected body's: transformed shape, linear velocity and fill color.
  • They will remove the selected body (and its dependencies) from the Scene.
  • They will insert the new LiquidNode or ElasticNode in the Scene.

Note that in contrast with most commands that append new nodes in the Scene, these commands will insert the new Node instead. The reason this is done is to have all liquids and soft bodies stacked behind rigid bodies (which looks more realistic).

Scene Updates

The main change in the Scene node is the addition of a particle system. This means that when a new Scene is constructed a new particle system will be constructed with it. All liquids and elastic bodies that will be created by the user will belong to this particle system. A particle system has many options that can be defined but for the time being these options will not be exposed to the user nor they will be saved in the Scene (this might be done in the future).

Also, a new property was added in the Scene. Its name is particleIterations and its default value is 3. The higher this value is the better (more accurate) the particle simulation will be.

ParticleGroupNode

The ParticleGroupNode serves as the base class for both LiquidNode and ElasticNode.

As the name suggests, this node represents a group of particles.

Each particle is round, has a fixed rotation and has the following properties:

  • Position
  • Velocity
  • Color

Because both of the liquify and elastify commands produce lots of particles (typically in the range of thousands) we need to better handle how the ParticleGroupNode is saved. In order to solve this it was decided to use (lz) compression when packing the Scene.

Some general key points for the ParticleGroupNode are:

  • Its x, y and angle properties are always zero and cannot be changed.
  • Its bounding rectangle is defined by the positions of the particles it contains.

Selection

As mentioned above, the ParticleGroupNode is a group of particles. For the time being, the user can only select the group as a whole and individual particles within the group cannot be selected.

Rendering

Ideally, for rendering the particles we would use shaders but since shaders are not yet supported in Physion we will use a simpler approach. To render each particle a single sprite will be used.

The texture for this sprite looks like:

particle

Limitations

  • A ParticleGroupNode cannot be moved or mirrored.

Future Work

  • Use shaders to better visualize liquid and elastic bodies.
  • Expose more LiquidFun properties (Particle System/Group) in the UI.

· One min read
Physion

A new (BodyNode) property was recently added in Physion.

Its name: Gravity Scale (gravityScale).

As the name suggests, the gravityScale property of a body can be used to "scale" the gravitational attraction applied to it. In other words, the gravityScale allows you to specify a different gravity for each body.

By default, the value of this new property is 1.0 meaning that the Scene's gravity will be applied to the body. If you change this value to say 0.5 then only half of the Scene's gravitational force will be applied to the body. If you want to make a body float (i.e ignore gravity) you can set a gravity scale of 0. To make a balloon-like body you can set its gravityScale to a negative number.

In the video below you can see the gravity scale in action:

Note that the minimum value of this property is -1 and its maximum value is +1.

Using this new property and some minimal scripting, the user can easily create gravity pads which are very useful in marble races and other similar simulations.

· 4 min read
Physion

Recently, a feature request was made in Physion's Discord Server: "Add the ability to visualize the trajectory of an object". This functionality was already implemented in the old version of Physion as you can see in the video below:

Unfortunately (and for the time being) the web version of Physion doesn't natively support tracers. The goods news is that we can implement the tracer functionality using scripting.

The Tracer Script

We're going to create a NodeScript (i.e. a script that gets attached to a Node) to implement the tracer functionality.

Let's start with the constructor:

 class Tracer {

constructor(node) {
this.node = node;
this.initialized = false;

this.trajectory = [];
this.maxTrajectorySize = 100;

if (typeof physion !== "undefined") {
this.graphics = physion.utils.createGraphics();
this.lineStyle = physion.utils.createLineStyle(0);
this.fillStyle = physion.utils.createFillStyle(0xff0000);
}

this.tracerRadius = 0.1;
}

// ..

}

In the constructor we initialize the data members of the class that we're going to use:

Data memberDescription
nodeA reference to the node that this script is attached to (passed as argument in the constructor).
initializedBoolean switch to help us perform a "one-off" initialization (see below).
trajectoryThe trajectory array which is going to hold the positions (x, y) of our node as the simulation runs.
maxTrajectorySizeThe maximum length of the trajectory array. The bigger this value is the bigger the trace will be.
graphicsThis is the 'canvas' where the trace will be drawn.
lineStyleThe line style to be used when drawing the tracer (line width of 0 pixels, i.e. don't draw outline)
fillStyleThe fill style to be used when drawing the tracer (red color)
tracerRadiusThe radius of the circles used when drawing the tracer.

The update() method of the script is shown below:

update(delta) {
if (!this.initialized) {
this.initialized = true;
const scene = this.node.findSceneNode();
if (scene) {
scene.layers.foreground.addChild(this.graphics);
}
}

this.updateTrajectory();
this.drawTracer();
}

First, we use our initialized data member to perform a 'one-off' initialization task: Add our graphics to the foreground layer of the scene (that our node belongs to). In order to retrieve the scene that our node belongs to, we use the findscenenode method.

After initialization is performed, the script keeps repeating the following:

  • Updates the trajectory array
  • Draws the tracer

The updateTrajectory() method is responsible to update our trajectory array in each update. It does that by pushing the latest position of our node to the array. It also makes sure that this array doesn't grow infinetely: If the size of the trajectory array gets bigger than maxTrajectorySize then old positions will get removed.

updateTrajectory() {
this.trajectory.push(this.node.getPosition());
while (this.trajectory.length > this.maxTrajectorySize) {
this.trajectory.shift();
}
}

Finally we call the drawTrajectory helper which will use the trajectory array to draw a series of circles thus visualizing the trajectory of our node:

drawTracer() {
if (this.graphics && this.fillStyle) {
this.graphics.clear();
this.trajectory.forEach((point, i) => {
this.fillStyle.alpha = (i + 1) / this.trajectory.length;
physion.utils.drawStyledCircle(this.graphics, point.x, point.y, this.tracerRadius, this.lineStyle, this.fillStyle);
});
}
}

Using the Tracer Script

The Tracer script is available in the Assets Browser. To use it simply drag and drop the script onto the node to create the tracer for.

You can also see the tracer script in action in this sample scene.

Future Work

  • Add the ability to trace any point on an object (and not just its center as presented in this solution).
  • Add a TracerNode (and the corresponding UI) to allow creating tracers natively (i.e. without a custom script)
  • More customization (e.g. gradient color)