| Engine | Godot 4.2 or newer |
|---|---|
| Language | GDScript |
| Attach to | Camera2D |
| Time to run | ~2 minutes |
| Level | Beginner |
| Output | One file (~45 lines) |
| Dependencies | None. FastNoiseLite is built in. |
The prompt
Open a fresh chat at claude.ai/new, paste, and send.
$ Build me a Godot 4.2+ Camera2D screen shake as a single GDScript, using the trauma model. # shape - One script, file shake_camera.gd, attached to a Camera2D. - No plugins. # trauma - A trauma float, 0 to 1, that decays every frame by a @export decay rate (default 1.2 per second). - add_trauma(amount: float) adds and clamps to 1. # shake - Each _process frame, compute shake = trauma * trauma (squared so small trauma is subtle). - Offset the camera by shake * max_offset, where max_offset is an @export Vector2 (default 24, 16). - Sample two FastNoiseLite channels (or one noise at two seeds) over time * a @export frequency for the x and y offset, so the motion is smooth noise, not per-frame random. - Optionally add a small rotation: shake * @export max_roll (default 0.05 rad). - When trauma is 0, offset and rotation return to zero exactly. # constraints - Godot 4 API (Camera2D.offset, FastNoiseLite). - No magic numbers. Everything tunable is @export. # return format - One file. No prose before or after the code. - Brief comments only on the trauma-squared line and the noise sampling.
What this gets you
The difference between a hit that feels like a hit and a number going down. Call add_trauma(0.3) on a light hit and add_trauma(0.8) on a big one, and the screen responds proportionally, then settles. No lingering wobble, no jarring snap.
Because it is on the camera and driven by a single add_trauma call, anything in your game can shake the screen without knowing anything about the camera.
Why it matters
Trauma squared
Linear shake makes every hit feel the same. Squaring trauma means a 0.3 tap barely nudges the screen while a 0.9 slam rattles it, which matches how impacts actually feel. It is one character of code with an outsized effect.
Noise over random
Reassigning a random offset each frame is the number one reason homemade shake looks cheap, it is visual static. Smooth noise sampled over time gives a believable physical wobble.
Decay built in
Because trauma decays on its own, callers never have to stop the shake. They add an impulse and forget it. That is what makes it drop-in anywhere.
What good output looks like
- Shake uses trauma squared (or cubed). Look for
trauma * trauma, not raw trauma. Linear trauma is the tell of a copied-from-random tutorial. - Offset comes from noise sampled over time. You should see a FastNoiseLite sample using a time accumulator, not
randf_rangeevery frame. - Trauma is clamped and decays in _process.
trauma = clamp(trauma - decay * delta, 0, 1)or equivalent. If it never decays, the screen shakes forever. - Returns to exactly zero. When trauma hits 0, offset and rotation must be set to zero, not left at the last noise value, or the camera stays slightly crooked.
How to wire it up
- Save the output as
shake_camera.gd. - Attach it to your
Camera2D(the one that follows the player). - From any script with a reference to the camera, call
camera.add_trauma(0.5)on an impact. - Tune
max_offset,decayandfrequencyin the inspector until it feels right for your game.
Common gotchas
The shake never stops
Trauma is not decaying. Confirm the decay line runs in _process every frame and that nothing is topping trauma back up.
It looks like buzzing static
You are using random offset instead of noise. Switch to sampling FastNoiseLite over a time accumulator.
The camera drifts off-center
The offset is being added to itself instead of set fresh each frame, or it does not reset to zero at trauma 0. Set offset = computed value each frame, do not accumulate.
Where to go next
- Directional shake. Bias the offset along the hit direction for a punchier feel.
- Hit stop. Pair a tiny
Engine.time_scaledip with the trauma for extra impact. - Rumble. Trigger controller vibration from the same add_trauma call.
Use it however you want
The prompt is CC0, paste it, modify it, ship a game with it. The output Claude gives you is yours. A link back to pixeldex.dev in your project's README helps the next solo dev find this stuff.