Creating a Spooky Parallax Effect in Phaser 3:

My Witch’s Night Game

In this blog post, I’ll walk you through the process I used to create this parallax effect in Phaser 3, along with the code snippets to bring it all together.

All the illustrations and artwork in the game are entirely my original work, created both digitally and on paper. These illustrations have been with me for about 15 years, and they’ve finally found their place in this interactive world.

For my Halloween-themed game Witch's Night, I wanted to achieve a dynamic parallax effect that would create depth and movement in the scene. The effect would consist of three layers—foreground, midground, and background—each scrolling at different speeds. I also integrated interactive objects within the foreground layer, making the scene come alive with animations and sound effects.

The Setup

I structured the parallax effect using Phaser’s tileSprite objects, which are ideal for scrolling backgrounds. Here’s a quick overview of how the layers work:

  1. Background: Moves the slowest, giving the impression of distant scenery.
  2. Midground: Moves slightly faster than the background.
  3. Foreground: Moves the fastest and includes interactive objects that respond to user inputs.

Let’s take a look at the core function responsible for setting up the parallax:

create() {
  // Background layer (farthest from the player)
  this.sky = this.add.tileSprite(0, 0, 3071, 417, "sky").setOrigin(0, 0).setDepth(0);
  this.sky.setScale(1.2);

  // Midground layer
  this.midground = this.add.tileSprite(-350, this.scale.height - 770, this.scale.width, 447, "midground")
                      .setOrigin(0, 0)
                      .setDepth(2);
  this.midground.setScale(1.2);

  // Foreground layer (closest to the player, includes interactive elements)
  this.foreground = this.add.tileSprite(1050, 75, 3071, 476, "foreground")
                        .setOrigin(0, 0)
                        .setDepth(3);
  this.foreground.setScale(1.3);
}

Adding Motion

To create the scrolling effect, I update the x-coordinates of each layer in the update method, where the speed varies depending on the depth of the layer:

update() {
  this.sky.tilePositionX -= 0.2; // Slowest movement
  this.midground.tilePositionX -= 0.4; // Moderate speed
  this.foreground.tilePositionX -= 0.8; // Fastest speed
}

This results in a smooth parallax effect where each layer moves at a different speed, enhancing the sense of depth.

Adding Interactive Elements

I wanted the player to feel engaged with the scene, so I added various interactive objects—witches, trees, a piano, and more—that respond to user actions like clicks and hovers. Using a custom utility function, setupInteractiveAnimation, I ensured that each object has unique animations and sounds.

Example: Interactive Piano

For the interactive piano, I created an animation and played a sound effect whenever the player clicks on it. Here’s the setup:

// Create static piano image and set interactivity
this.staticPiano = this.add.image(970, 600, "staticPiano").setInteractive({ cursor: "pointer" }).setDepth(10);

// Define piano animation
this.anims.create({
  key: "pianoAnim",
  frames: this.anims.generateFrameNumbers("pianoAnim", { start: 0, end: 4 }),
  frameRate: 3,
  repeat: 3
});

// Use utility function to handle interactivity and animation
setupInteractiveAnimation(
  this,
  this.staticPiano,
  "pianoAnim",
  "pianoSound",
  this.staticPiano.x,
  this.staticPiano.y,
  10,
  1 // scale
);

The setupInteractiveAnimation function simplifies setting up animations and sounds for each object, making it easy to add interactive elements with minimal code repetition.

Interactive Lantern

I also added a glowing lantern in the foreground. It gently sways back and forth, and when clicked, it reveals a hidden clue. This small interaction adds an element of mystery:

this.lantern = this.add.image(100, 0, "lantern")
                  .setOrigin(0.5, 0)
                  .setDepth(19)
                  .setInteractive({ cursor: "pointer" });

this.tweens.add({
  targets: this.lantern,
  angle: { from: -7, to: 7 },
  duration: 2000,
  yoyo: true,
  repeat: -1,
  ease: "Sine.easeInOut"
});

// Show clue on click
this.lantern.on("pointerdown", () => {
  this.showClue("yellow"); // Custom function to show clues
});

The combination of parallax and interactivity resulted in a rich and engaging visual experience for the player.

Conclusion

The parallax effect in Witch's Night not only brings depth to the scene but also sets the stage for an interactive environment filled with surprises. With a few tweaks, you can create similar effects for your own projects using Phaser 3!

If you’re interested in trying out the game or have any questions about the code, feel free to drop a comment below or check out the live demo here.

Happy coding, and have a spooky Halloween! 🎃

Rome visit


I’ve been away for a bit… visiting Rome and Pompeii.
Short and lovely vacation.

The art of acting

According to Yehuda Bauer, in Holocaust we can find elements that can not be found anywhere else, that are totally unprecedented. Victims were all Jews, people who had one or more than one grandparent. Wherever they could, Nazis identified and defined Jews: who is Jew, who is quarter Jew, half Jew. Then, Jews were defined, marked, dispossessed, humiliated, concentrated, transported and killed.
Every single person Nazis could reach. Everywhere in the world.

“Nazis had shiny boots and they were looking through us”, said Nechama Tec.
She also said she had never seen that kind of behavior before.

Another Holocaust survivor, Paula Lebovics, in her testimony said, “Even today I can hear those boots, and it chills me to the bones.”

Besides that, Nazis wore neat, ironed suits. It was part of their presentation. They wanted to seed a fear.
In the Auschwitz camp there weren’t so many German guards. Everything was organized, so as they needed more men or women, they used Jews or other prisoners.
And they did their best to seed a fear. They used sticks, guns and sound of their boots was one of their weapons.
Daily killings were part of the ritual to seed a fear.
Another Nazi weapon — there were signs. Signs were everywhere.

On the entrance there was slogan “Arbeit Macht Frei” that means “labour makes (you) free”.
Even people who were arriving, were deceived. They thought that they are coming to work.
Germans acted as if they are not performing such atrocity. Nobody believed that it was happening. Not citizens of Europe or people in America.
Why?
It was important that nobody finds out. If it gets revealed, they won’t have such a success rate or no success at all.
So, it was secret for several years. There were rumors, but nobody actually knew. They succeeded in their deceit.
5.7 million people were killed, gassed, shot.
On the other hand, people like Nechama Tec, had a slight chance – it was to act, act like they are somebody else. Pretending she is a Polish girl was her salvation.
Or like grandfather in the movie “Everything is Illuminated”, who acted as if he’s anti-Semite, whilst in reality he was a Jew. He had his reasons. Around himself he had made impenetrable shell. He even fooled himself. It was his life time burden. He felt guilt – he was alive but he did not deserve to live – it was his opinion (as Jew). So, he passed in his reality to anti-Semite. Until revelation and recognition that he has no guilt. His only guilt was that he is a Jew.
Or like Paula, who said, “I made myself invisible”. She floated in her mind somewhere else and although she witnessed horrid, unbelievable events, she learnt to become invisible. Encounters with death were frequent. The only thing victims could rely on was hope.
And that was ephemeral.
Those who did not lose their faith and hope had no chance at all.

#556

Composition X 1939The Brain, within its Groove
Runs evenly–and true–
But let a Splinter swerve–
‘Twere easier for You–

To put a Current back–
When Floods have slit the Hills–
And scooped a Turnpike for Themselves–
And trodden out the Mills–

#556
by Emily Dickinson