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! 🎃

Mending Wall

by ROBERT FROST

Something there is that doesn’t love a wall,
That sends the frozen-ground-swell under it,
And spills the upper boulders in the sun;
And makes gaps even two can pass abreast.
The work of hunters is another thing:
I have come after them and made repair
Where they have left not one stone on a stone,
But they would have the rabbit out of hiding,
To please the yelping dogs. The gaps I mean,
No one has seen them made or heard them made,
But at spring mending-time we find them there.
I let my neighbor know beyond the hill;
And on a day we meet to walk the line
And set the wall between us once again.
We keep the wall between us as we go.
To each the boulders that have fallen to each.
And some are loaves and some so nearly balls
We have to use a spell to make them balance:
‘Stay where you are until our backs are turned!’
We wear our fingers rough with handling them.
Oh, just another kind of out-door game,
One on a side. It comes to little more:
There where it is we do not need the wall:
He is all pine and I am apple orchard.
My apple trees will never get across
And eat the cones under his pines, I tell him.
He only says, ‘Good fences make good neighbors.’
Spring is the mischief in me, and I wonder
If I could put a notion in his head:
‘Why do they make good neighbors? Isn’t it
Where there are cows? But here there are no cows.
Before I built a wall I’d ask to know
What I was walling in or walling out,
And to whom I was like to give offense.
Something there is that doesn't love a wall,
That wants it down.’ I could say ‘Elves’ to him,
But it’s not elves exactly, and I’d rather
He said it for himself. I see him there
Bringing a stone grasped firmly by the top
In each hand, like an old-stone savage armed.
He moves in darkness as it seems to me,
Not of woods only and the shade of trees.
He will not go behind his father’s saying,
And he likes having thought of it so well
He says again, ‘Good fences make good neighbors.’

Dance

Нити нити

Јооој овако се кућа стече
У кафани свако вече
Данас мало, сутра мало
Прекосутра још по мало

Јооој, јоој

Научи се вино пити
Па продадо’ жени нити
Стаде мене жена бити
Што про продадох њене нити

Јооој, јоој

Немој жено, ја те молим
Немој жено, ја те волим
Купићу ти нове нити
Па ћу с тобом уводити

Јооој, јоој

Купићу ти нове нити
Па ћу с тобом уводити
Даваћу ти сваку жицу
Играћу ти кукуљицу

Performed by:
Етномузиколошки одсек Музичке школе “Мокрањац”, Београд @stevan.stojanovic.mokranjac

Solstice

In the Northern Hemisphere, the December Solstice is the winter solstice and the shortest day of the year.
In the Southern Hemisphere, it is summer solstice and the longest day of the year.

The term solstice comes from the Latin word solstitium, meaning ‘the Sun stands still’.
This is because on this day, the Sun reaches its southern-most position as seen from the Earth.
The Sun seems to stand still at the Tropic of Capricorn and then reverses its direction.

It’s also common to call it the day the Sun turns around.