# Chapter 10: Game feel and juice (/guides/handmade-web-games/techniques-for-game-feel-and-juice)





## Juice? [#juice]

In game design, **juice** refers to the visual, audio, and controller feedback added to make a game feel alive and highly satisfying to play. It's not just about making things flashy and loud, though. Done well, juice makes your game more immersive, interesting, and intuitive to the player.

<PongJuiceCompareDemo />

To demonstrate a few of the techniques we'll explore in this chapter, our juiced Pong includes impact particles, screen shake, bullet time, and scaling up on impact. And of course explosions. These effects increase with the speed of the ball so that the intensity of the effects ramps up gradually as the game itself gets more intense.

The thing with feedback is that it's best if you can hit as many senses as possible. We often think about the visuals for games, but touch and sound are just as important for game feel.

## Haptic feedback [#haptic-feedback]

If you've added controller support, then you can deliver touch feedback through controller haptics. (And if you haven't yet added controller support, you should! It's fun and easy--see [Chapter 8](/guides/handmade-web-games/controllers-and-haptics).) Haptics provide a low-effort way to increase the immersiveness of your game through tactile feedback. Our juiced pong has haptics enabled, too!

## Audio feedback [#audio-feedback]

Some basic tips when providing audio feedback in games:

* Randomize the pitch or speed of repetitive sounds, or loop through several audio samples. (Or both!)
* If you have overlapping sounds, run them through a compressor so the total volume doesn't grow too high.
* Play sounds for state changes and collisions, especially when triggered by the player. Even hover effects deserve subtle audio cues.

## Visual feedback [#visual-feedback]

### Anticipation [#anticipation]

(todo: two cannon fire demos. one charges up and animates before firing, the other does not.)

### Squash and stretch on impact [#squash-and-stretch-on-impact]

(todo: bumping into walls with squash and without)

### Screen shake [#screen-shake]

(todo: demo + example code, based on the WIP cameras chapter)

### Freeze on impact [#freeze-on-impact]

(todo: example code adding simulated pause time to the accumulator)

### Particles [#particles]

Particle effects are a great way to add juice to your games. Impact sparks, smoke, tire dust, muzzle flashes, blood splats, water spray, jump clouds, explosions, fire, vehicle exhaust, and ambient weather like rain or snowfall can all be implemented with particle emitters. So can fire and smoke emissions from a rocket:

<Ld59RocketDemo />

<Callout type="info">
  This is the code that got me excited about particle effects. It's adapted from [The
  Transmitter](https://transmitter.onsclom.net/), a submission to the [Ludum Dare
  59](https://ldjam.com/events/ludum-dare/59/games/overall/compo) game jam by a co-author of this
  guide, [Austin @onsclom](https://www.onsclom.net/).
</Callout>

The basic technique is to start with some state similar to our bouncing ball from [Chapter 4](/guides/handmade-web-games/animation).

```ts
const particle = {
  x: 0,
  y: 0,
  vx: 0,
  vy: 0,
  age: 0,
  life: 0,
  // plus any other properties you want...
  color: "orange",
  fromSize: 10,
  toSize: 0,
  fromOpacity: 1,
  toOpacity: 0,
};
```

The main new concepts here are `age` and `life`.

* `age` ticks up with `dt` once per update
* `life` is how long the particle lasts
* when `age > life` we skip updating and rendering
* any `from*` and `to*` values are `lerp`ed using `t = age / life`

The state from this particle provides settings for size and opacity changes over the particle's life, but you can add anything you want to it. Maybe you want to be able to transition the colors, add rotation speed, use a non-linear easing curve--when you define your own particle system, you decide exactly what goes into it.

Using this state, let's spawn some particles.

<Scrollycoding preview="canvas2d">
  <slot>
    <>    </>
  </slot>

  <slot path="steps">
    <>
      We'll start by emitting just one particle on an interval from the center of the canvas. This code should look pretty familiar if you followed the earlier example with the ball.

      You'll notice we're using the variable frame-rate `update` function rather than `fixedUpdate` since particles tend to be used for visual flare rather than for game logic that requires fixed time steps. This means we can avoid updating the particles' positions more frequently than they're rendered.
    </>
  </slot>

  <slot path="steps">
    Next we'll add a few properties to allow us to transition the size and opacity of the particle over the course of its `life`. Here we're animating the size and opacity, but you can update any properties you want or even derive them without adding state.
  </slot>

  <slot path="steps">
    <>
      Because they're small and ubiquitous, it's easy to end up with thousands of particles on screen. Assuming updates run at 120fps, that could involve hundreds of thousands of object allocations and dynamic array resizes per second. That can be a problem when the browser decides it is time to run the [garbage collector](https://en.wikipedia.org/wiki/Garbage_collection_\(computer_science\)) <small className="opacity-75">\[Wikipedia]</small>. If it takes too long to collect and free unused objects, you may drop a frame or two periodically.

      We'll mitigate this issue by creating our particles up front. The idea is to preallocate one big array for all the particles you could ever need, and generate the objects in it just once. This approach, called a ring buffer or [circular buffer](https://en.wikipedia.org/wiki/Circular_buffer) <small className="opacity-75">\[Wikipedia]</small>, is an optimization. So far in this guide we've been fairly oblivious to performance concerns, but when you might be spawning thousands of particles 120 times per second, particle emitter code is likely to be the hottest loop in your game code. The ring buffer reduces potential dropped frames that can happen during garbage collection by preventing said "garbage" from being created in the first place. Operations like `.filter` or `.map` and building new objects all allocate new memory that needs to be cleaned up by the GC. By making a fixed-size array with all the particle objects in it up front we eliminate that work for the GC entirely.

      <details>
        <summary>
          <strong>
            Is this premature optimization?
          </strong>
        </summary>

        Our [benchmark](https://particle-benchmark.spud.gg/) aims to answer that question.

        In my browser, the benchmark shows that a ring buffer ensures relatively stable `update` times whereas a `push` + `filter` approach occasionally slows down during GC--at least, for very high particle counts. Most small games don't need the number of particles required to really see the difference here, though. That being said, the more memory your game uses, the slower garbage collection gets. So what might be negligible GC time when you're starting out your project could result in many dropped frames as your code grows in complexity later on. Tracing sources of slow GC is arguably way harder than using a ring buffer, so I'd say that this is a worthwhile optimization to include.

        I will note two minor inconveniences with the ring buffer approach, though: (1) you need to estimate a reasonable upper bound on the total particles up-front, and (2) you need to clear out stale properties when you initialize a new particle.

        If you'd like to learn more about how V8's Orinoco garbage collector works, [Playing with Garbage](https://www.youtube.com/watch?v=easvMCCBFkQ) <small className="opacity-75">\[YouTube]</small> by SimonDev provides a great rundown.
      </details>
    </>
  </slot>

  <slot path="steps">
    The `"lighter"` blend mode is often used for fire and sparks since it makes overlapping particles brighter. We'll increase the number of particles on screen to highlight the effect.
  </slot>
</Scrollycoding>

## Game feel [#game-feel]

* input buffering
* coyote time

## Up next [#up-next]

We've covered a handful of techniques to make your game feel polished. In [Chapter 11 →](/guides/handmade-web-games/offline-mode) we'll look at a different kind of polish: making your game installable and playable even offline. PWAs and cached assets also speed up subsequent boots of your game, so it can load almost instantly.

## References [#references]

* [Celeste & Forgiveness](https://www.maddymakesgames.com/articles/celeste_and_forgiveness/index.html) by Maddy Thorson

[https://garden.bradwoods.io/notes/design/juice](https://garden.bradwoods.io/notes/design/juice)

[https://www.youtube.com/watch?v=Fy0aCDmgnxg](https://www.youtube.com/watch?v=Fy0aCDmgnxg)

[https://www.youtube.com/watch?v=AJdEqssNZ-U](https://www.youtube.com/watch?v=AJdEqssNZ-U)

in some other chapter--tile maps: [https://developer.mozilla.org/en-US/docs/Games/Techniques/Tilemaps](https://developer.mozilla.org/en-US/docs/Games/Techniques/Tilemaps)
