Visual Design • Polish

The Art of Game Juice: Tweening, Shake & Polish

Written by Sudhishkumar K • July 2026 • 25 Min Read

Imagine a game of Breakout. A ball hits a brick, the brick disappears, and the score goes up by 10. This is functional. It works.

Now imagine the same game, but when the ball hits the brick:
1. The brick flashes white before vanishing.
2. The screen shakes violently for 0.1 seconds.
3. A particle explosion of crumbs flies outward.
4. The ball stretches (squash & stretch) upon impact.
5. The game freezes for 3 frames (Hit Stop) to emphasize the power.

This is Juice. Juice (or "Game Feel") is the non-functional feedback that bridges the gap between the player's brain and the digital world. It is the difference between a student prototype and a Top-Grossing hit on the App Store.

At Boomie Studio, we follow a strict "Juice Protocol." A mechanic isn't finished until it feels satisfying to interact with. In this guide, we will break down the mathematical and technical implementation of Juice in Unity.

1. Tweening: The End of Linear Movement

In the real world, nothing moves linearly. When you open a drawer, you pull it fast, and it slows down as it opens. When a ball falls, it accelerates.

If you write code like this, you are creating robotic movement:

transform.position = Vector3.MoveTowards(start, end, speed * Time.deltaTime);

To make things feel organic, you need Procedural Animation, commonly known as Tweening. We recommend DOTween (free or Pro) for Unity.

The Magic of Easing

The secret sauce of tweening is the "Ease Function."

Ease.OutQuad Starts fast, slows down at the end. Use this for sliding UI panels or characters stopping. It feels natural and smooth.
Ease.OutBack Goes past the destination and snaps back. Use this for pop-ups, buttons, and item pickups. It feels energetic and playful.
Ease.InExpo Starts very slow, then explodes into speed. Use this for rockets launching or an enemy charging an attack.
Ease.OutElastic Wobbles like jelly. Use this sparingly for "cartoony" impacts.

// Example: A satisfying UI Popup
transform.localScale = Vector3.zero; // Start invisible
transform.DOScale(Vector3.one, 0.4f).SetEase(Ease.OutBack);
            

2. Advanced Screen Shake (The "Trauma" System)

Screen shake is easy to do, but hard to do right. Beginners usually just move the camera to a random position.

The Problem: If you use Random.InsideUnitCircle, the camera jitters uncontrollably like a broken strobe light. It feels glitchy, not heavy.
The Solution: Use Perlin Noise and the Trauma concept (popularized by GDC talks).

The Trauma Math

Instead of setting "Shake Amount," we set a value called Trauma (0 to 1). Every frame, Trauma decreases linearly.
The actual shake is calculated as Trauma squared (Trauma²).

Why square it?
Trauma 1.0 -> Shake 1.0 (Massive explosion)
Trauma 0.5 -> Shake 0.25 (Mid-level rumble)
Trauma 0.2 -> Shake 0.04 (Barely noticeable)
This creates a nice, smooth falloff where big explosions feel huge, but the shake fades out quickly and subtly.


// CameraShake.cs
public class CameraShake : MonoBehaviour {
    public float Trauma = 0;
    public float Decay = 1.5f;
    public float MaxAngle = 5f;
    public float MaxOffset = 0.5f;

    void Update() {
        if (Trauma > 0) Trauma -= Time.deltaTime * Decay;
        
        // Square the trauma for the shake power
        float shake = Trauma * Trauma;

        // Use Perlin Noise for smooth, organic movement
        // We use Time.time * speed to scroll through the noise map
        float angle = MaxAngle * shake * (Mathf.PerlinNoise(0, Time.time * 20) * 2 - 1);
        float offsetX = MaxOffset * shake * (Mathf.PerlinNoise(1, Time.time * 20) * 2 - 1);
        float offsetY = MaxOffset * shake * (Mathf.PerlinNoise(2, Time.time * 20) * 2 - 1);

        transform.localPosition = new Vector3(offsetX, offsetY, 0);
        transform.localRotation = Quaternion.Euler(0, 0, angle);
    }
    
    public void AddTrauma(float amount) {
        Trauma = Mathf.Clamp01(Trauma + amount);
    }
}
            

3. Hit Stop (Freeze Frames)

Go play Zelda or Street Fighter. When a sword connects with an enemy, the game literally pauses. The animation freezes.

This is called Hit Stop (or Hit Pause). It gives the player's brain a fraction of a second to process "I successfully hit the target." Without it, combat feels like slicing through air (the "butter knife" effect).

Implementation

The easiest way is using Time.timeScale. However, use this carefully, as it pauses everything (including music/UI if not configured correctly).


public void ApplyHitStop(float duration) {
    StartCoroutine(DoHitStop(duration));
}

IEnumerator DoHitStop(float duration) {
    // 1. Freeze
    Time.timeScale = 0.0f;
    
    // 2. Wait (Use realtime, because game time is frozen!)
    yield return new WaitForSecondsRealtime(duration);
    
    // 3. Resume
    Time.timeScale = 1.0f;
}
            

Pro Tip: For a light punch, use 0.05s. For a heavy critical hit, use 0.15s to 0.2s. Never go above 0.3s or the game feels unresponsive.

4. Squash and Stretch

This is the #1 principle of animation. Objects should deform when they move to show speed and weight.

You don't need a rigged 3D model for this. You can do it procedurally on the Transform logic itself.

Volume Conservation: If you stretch Y by 20%, you must squash X by roughly 20%. The total mass of the object should appear constant.

5. Chromatic Aberration & Post Processing

Unity's Post-Processing Stack is free "Juice."

Chromatic Aberration: This effect separates the Red, Green, and Blue channels of the image, simulating a broken camera lens.
How to use it: Usually, keep it at 0. But when the player takes damage or boosts, tween the value to 1.0 and back to 0 quickly. It creates a subconscious "glitch" effect that feels painful or incredibly fast.

Bloom: Essential for anything "Neon." Set your material Emission intensity higher than 1 (HDR), and the Bloom will make it glow, bleeding light into neighboring pixels.

6. Input Buffering (Invisible Juice)

Juice isn't just visual; it's also about control feel.

Coyote Time: Allow the player to jump for 0.1s after they have walked off a ledge. This prevents the frustration of "I swear I pressed jump!"

Jump Buffering: If the player presses Jump 0.1s before hitting the ground, remember that input and execute the jump the moment they land.

These invisible helpers make the game feel "responsive" and "tight," even though they are technically cheating in the player's favor.

Conclusion

Polish is not something you do at the end of the project; it is a mindset. A button isn't done when it clicks; it's done when it pops, clicks, and creates a satisfying ripple.

Start small. Add DOTween to your UI today. Add a tiny screen shake to your player's death. You will immediately notice that your game doesn't just look better—it feels expensive.


Juice Checklist