Phasmophobia and Bézier Curves

I’ve been playing a lot of Phasmophobia recently. It’s a ghost-hunting game where you, and a team of up-to 3 other investigators explore haunted properties looking for evidence in order to determine what type of ghost is present. At the same time you have to try to not get killed at the hands of said ghost.

So what does this game have to do with Bézier curves??

We’ll get there. Trust me.

In the most recent release, the developers added in some riddles and clues to tease something coming in the next major revision. The problem I ran into was that if I had a question about a riddle I couldn’t solve, googling would only bring up spoiler-filled results. I don’t want answers, but little nudges in the correct direction.

I decided to take it upon myself to create a repository of information about the clues so one can decide which information to be exposed to, thereby minimizing risk of unwanted spoilage. I give you, Phasmophobia Rune Hints.

This still has nothing to do with Bézier curves! Get on with it!

Dear reader, I am nearly there. Believe me!

One of the evidences of ghost ghost activity in the game is what they call “Ghost Orbs”. They are floating balls of light that are only visible on video camera. (In reality these are specs of dust reflecting light close to a lens…) In any case, I wanted to spruce up my rune hinting website with some Phasmophobia appropriate ambiance. I wanted to add some ghost orbs.

My first implementation was to take a PNG of a ghost orb and use javascript to move it in a specific direction while fading it in and out at the ends of the animation. This yielded a result and was maybe passable, but it wasn’t great. My brother suggested to use Bézier curves in order to have it follow a more natural curved path.

In this example, the ghost orbs both start and end at the same location, however the one on the left follows a linear path while the one on the right follows a Bézier curve. (The points are chosen at random, so you may have to watch a few cycles to get an interesting comparison). Below is a simplified example of the code to demonstrate the minimal logic required to compute the curved path.

function find_point_on_line(p1, p2, percent){
    let p3 = {
        "x" : ((p2.x - p1.x) * percent) + p1.x,
        "y" : ((p2.y - p1.y) * percent) + p1.y,
    return p3;

function follow_bezier_curve(b1, b2, b3, percent){
    if (percent >= 1) return; // We finished
    let p1 = find_point_on_line(b1, b2, percent);
    let p2 = find_point_on_line(b2, b3, percent);
    let p3 = find_point_on_line(p1, p2, percent);
    timer = setTimeout(() => follow_bezier_curve(percent+.001), 1000/60);

Bézier curves are pretty common. I’ve been using them for decades in software like Adobe Illustrator or Affinity Designer. I remember learning about them in college. But I’ve actually never had a need to program my own.

As it turns out, they are really simple. A quadratic Bézier curve uses 3 points; a, b, and c. First you draw a path from a to b, and from b to c. Then you draw paths between points at equivalent subdivisions on these paths. The result is a really nice curve. It’s really hard to explain with words, so try out this interactive animation instead:

This is the type of Bézier curve I used on the Phasmophobia site.

There are also cubic Bézier curves. Cubic curves are basically two quadratic curves combined. Let’s say you have 4 paints; a, b, c, and d. You would create two quadratic curves; abc, bcd, then your final curve will be created by the same process along the resulting curves.

These 4-point quadratic curves are how vector drawing programs like Illustrator build paths. the outer two points act as nodes, and the inner two act as the handles.

Although not very complicated, I thought it was a fun diversion, and wanted to share.

More Thoughts on Lego

Quite a while back, I wrote about how much fun I was having with Lego Digital Designer (LDD). I was really pleased with that program, and have used it a lot (mostly to put together old sets from my childhood that I no longer have). Unfortunately Lego discontinued support in 2016. This wasn’t a huge deal at first; it just meant that new pieces weren’t being added. However, LDD for Mac is 32 bit, and Apple axed 32 bit support with the release of MacOS 10.15 Catalina. I actually abstained from upgrading to Catalina because of a few 32 bit apps, the most prominent of which was LDD. Eventually though, I had to upgrade and say goodbye to LDD.

Luckily, I found a way to fill the Lego shaped void in my heart. I discovered a program called Studio. Studio is developed by the people at Bricklink. It is very similar to LDD, but it is actually much better. It uses the LDRAW library of pieces so it’s parts inventory is much more complete than LDD’s.

It has a built in render function for making photo-realistic images of your creations that looks amazing. It’s really easy to use, and turns out great results.

You can import the pieces from real Lego sets by entering in the set number. All the pieces you need will be placed knolled on the build field. This is great for people who want to build sets virtually, like me, or for people who like to make instruction for My Own Creations (MOCs) where alternative builds are made using the same pieces from a real Lego set.

Overall, I think that Studio is fantastic, and I am so glad to have it available to use.

Other Thoughts on Lego

Lego has gone through many different generations and has evolved a bit with each one. I think any given generation will say the sets they grew up with were “the best,” but I find people are always most sentimental for what they had when growing up. Obviously though, what I grew up with in the 90’s was the best.


I played with Lego a lot as a kid, and I would build all sorts of things. Though I made a lot of spaceships and cars, I feel like a bulk of my free play was inventing mechanical things with Technic bricks. I remember making arms, gearing system, levers, and all sorts of interesting things.

I now play Lego with my kids, and one thing I find is how much I struggle to make mechanical things using the current Technic bricks. The bricks I am used to were just regular bricks with holes in them. My Technic cars would be two 16 long Technic bricks spaced apart and held together by a couple plates. It was quick and easy to build something like that. The new generation of Technic bricks are completely smooth, and my brain just didn’t get wired for dealing with it. I find it much more limiting somehow and struggle to build some things that I feel should be simple.

Collectible Sets

Another thing that feels like it’s changed since the 90’s is the proliferation of licensed and collectible/limited sets. The first licensed Lego set was Luke’s X-Wing (7140) which was introduced in 1999. Up to this point all Lego sets were all based on internal Lego themes, like Pirates, City, Space, etc. The popularity of licensed sets grew during the 2000’s. There were (and still are) licensed themes like Harry Potter, Star Wars, Minecraft, and Disney. And with the proliferation of these licensed sets came the behavior of collecting. People (nerds) want to display their sets or collect all their favorite things from their beloved franchises.

The old way of Legoing seemed to be buy -> build -> play -> destroy. Now a common progression is to buy -> build -> display. I am definitely guilty of this too. I have the Grogu set and you can bet I’m not letting the pieces get mixed in with everything else because it was a limited set and builds something super specific.

I recently read a comment on Hacker News where someone was lamenting that Lego sets have become 3D puzzles rather than a play system, and that sentiment really resonated with me.

Highly Specialized Pieces

Lego introduces new parts every year. They always have, and they always will. I think it’s good for things to grow and evolve, however it also feels like adding more and more parts dilutes the abstractability of the medium. Making very specific parts to a set that can only be used in a single way feels like it works against the free-play mentality that one associates with Lego.

This though really isn’t as much of a problem as one might think. If you are free-building with Lego and have these pieces, you either find creative ways to use them, or you ignore them. It’s a non-issue, but something I see come up a lot in discussions of modern-day Lego.

In any case, Lego remains one of my favorite things to play with, whether physically or digitally. It can be an open landscape for your imagination, and I’m glad that it’s available for me and my children to enjoy.

Reflected Primary Colors

Eyes are interesting things. We have color receptors in our eyes tuned for three different wavelengths. Short (blue), medium (green), and long (red). Our brains combine this information and allow us to perceive millions of color, which is really just amazing.

These three colors are so important, that we specifically target them when we produce images on our monitors and TVs. All the images are made up of these three colors. Just as importantly, when we capture images with cameras, we actually filter the light going into the cameras into the three primary colors.

Camera image sensors aren’t inherently color sensitive. Each pixel we get out of a camera is actually made up from information gathered from 4 sub-pixels on the digital camera sensor. Each sub pixel has a filter in front of it for light to pass through; 1 Red, 1 blue, and 2 green. This is called the Bayer Filter. After the camera takes a picture or records a frame, the sub pixels get interpolated together to give a single pixel of data.

(It’s probably more nuanced than that, but that is the general idea)

I recently purchased an RGB flashlight, and wondered how well I could reconstruct a color image by taking 3 photos illuminated with the different lights and combining them.

Ideally, I would like to try this using a black and white film camera, or black and white digital sensor, however I have access to neither, so I decided to use my iPhone.

The first method I used was to take color photos with each illumination, and layer them on top of each other using the add blend mode. Using this blend mode adds the RGB values together. For example:

This method works well. It’s somewhat surprising considering that nothing I used was color calibrated.

The second method that I was more excited about was taking black and white photos of each and using those as the raw channel data to reconstruct the image.

I took the photos on my iPhone while using the black and white filter mode. I then used Affinity Photo to import the photos and assign them to channels. The end result was abysmal.

I was able to improve it a bit by mixing the blue channel higher and reducing the intensity of the green channel.

Still not great.

I believe some of the problems are that the BW filter on the iPhone is not at all true black and white. I was surprised initially to find that the image wasn’t actually grayscale. It was RGB. I also don’t know how the image is being converted to black and white. It’s plausible that in the process of converting to grayscale, more red and blue data is thrown away in favor of green because it produces a better result to our eyes.

Although this was a bit of an interesting exercise, I think in the end it didn’t achieve great results because of the lack of true black and white sensor. The color results would have been more meaningful if it had been achieved without any color aware equipment.

Additionally, I’d be interested in comparing a composited photo using 3 exposures with red, green, and blue lights to a composited photo using 3 filters and white light. Someday I can revisit this experiment once I procure a proper camera.

© 2007-2015 Michael Caldwell