OPENRNDR & Processing - Bouncy Bubbles (JS)

It’s been a while since I’ve written these Java / Kotlin comparisons and today I felt like doing this, so here I am :slight_smile:

A tiny physics simulation. Shift+Click the image to run the program on a new window

Processing / Java

The original Bouncy Bubbles was written by Keith Peters / bit101. It was nice to discover this. He was an inspiring developer creating really cool stuff with Macromedia Flash back in the day, and he is still posting his experiments almost daily.

The code is quite simple, about 90 lines of code, featuring a Ball class with a constructor and three methods: collide, move and display.

OPENRNDR / Kotlin

Here is how I converted the code to Kotlin / OPENRNDR. Not much has changed, but lets look at the differences. I recommend viewing both versions side by side.

One thing to note is that Vector classes in OPENRNDR are not mutable, so you can’t just change their x or y value. That’s why velocity is a var, so I can rewrite it with a new value (by using += or *= for instance).

Using vector arithmetic makes the code shorter, because I do not need to operate on the x and y components separately: I can just add velocity to the position, for instance.

Another thing that makes the code shorter is that in Kotlin it’s easy to declare variables and initialize them at the same time. The balls array, which was declared at the top and then initialized in setup, is now just one line of code.
Also the Ball() constructor is much simpler, also just one line starting with class Ball(... instead of having a separate constructor like in Java.

I took the liberty to do two small changes regarding the collide method. One is to keep a safeArea variable per ball. This area equals the drawer bounds minus the radius of the ball. This allow me to write position = position.clamp(safeArea), instead of writing various x and y values depending on if the ball tries to escape left, right, up or down. The other minor change is about how friction is applied when hitting the borders: instead of checking if the value is less than radius or greater than width - radius, I check if the value is outside a range. I liked how that reads: if the x value is outside of the valid range, bounce.

imports
import org.openrndr.application
import org.openrndr.color.ColorRGBa
import org.openrndr.extra.noise.Random
import org.openrndr.extra.noise.shapes.uniform
import org.openrndr.math.Vector2
import org.openrndr.shape.clamp
fun main() = application {
    configure {
        width = 640
        height = 360
    }

    program {
        val numBalls = 12
        val spring = 0.05
        val gravity = Vector2(0.0, 0.03)
        val friction = -0.9

        class Ball(var position: Vector2, val radius: Double, val id: Int) {
            var velocity = Vector2.ZERO
            val safeArea = drawer.bounds.offsetEdges(-radius)

            fun collide(others: List<Ball>) {
                for (i in id + 1 until numBalls) {
                    val diff = others[i].position - position
                    val minDist = others[i].radius + radius
                    if (diff.length < minDist) {
                        val targetPos = position + diff.normalized * minDist
                        val acceleration = (targetPos - others[i].position) * spring
                        velocity -= acceleration
                        others[i].velocity += acceleration
                    }
                }
            }

            fun move() {
                velocity += gravity
                position += velocity
                if (position.x !in radius..width - radius) {
                    velocity *= Vector2(friction, 1.0)
                }
                if (position.y !in radius..height - radius) {
                    velocity *= Vector2(1.0, friction)
                }
                position = position.clamp(safeArea)
            }

            fun display() {
                drawer.circle(position, radius)
            }
        }

        val balls = List(numBalls) { Ball(drawer.bounds.uniform(), Random.double(15.0, 35.0), it) }

        extend {
            drawer.clear(ColorRGBa.BLACK)
            drawer.stroke = null
            drawer.fill = ColorRGBa.WHITE.opacify(0.8)
            balls.forEach {
                it.collide(balls)
                it.move()
                it.display()
            }
        }
    }
}

Online version in JavaScript?

In the good(?) old times, one could create Processing programs and post them online as Java Applets. This stopped working long time ago. Flash also is gone. So what if you want to share your OPENRNDR creations online?

openrndr-js-template to the rescue! It does not offer 100% of the functionality we have in the JVM side of things, but it does offer a lot. It takes advantage of the Multi-Platform capabilities of Kotlin to produce a JavaScript version of your program.

The small programs I experimented with produced files of ~300Kb. Not quite as small as with plain JavaScript, but much better than in my previous attempts with other frameworks, which produced files many megabytes heavy. Also much better than any news website out there :slight_smile:

:point_right: Run a the above program in your browser :point_left:

Normally OPENRNDR programs run locally in your computer. How did I create a web version? Quite simple:

  • Clone the JS template repo with git clone -b next-version https://github.com/openrndr/openrndr-js-template.git
  • Write your program in src/commonMain/kotlin/TemplateProgram.kt
  • See the instructions on how to run / publish a production version (HTML+JS)
  • Finally upload the result to a web server (I used GitHub pages in this case)

In the next weeks I plan to continue experimenting to see how much can one do with the JS version and post them online.

:point_down: Share your questions and comments below . :mag_right: Find other OPENRNDR & Processing posts

1 Like