How to Build a Godot Cinematic Camera for 3D Cutscenes

Getting a cutscene shot right means controlling more than the camera’s position. In this Godot cinematic camera tutorial, Zenva’s experience teaching coding and game development through over 400 courses guides a step-by-step approach to target tracking, handheld shake, and shot settings. You’ll then bring multiple viewpoints together with a camera manager that switches between them by ID.

This tutorial assumes you’re familiar with Godot and GDScript and are using the cutscene project’s existing 3D street scene, lights, and camera.

Download Project Files

The files and full project are available through the course included in the Godot 4 Game Development Mini-Degree if you want to follow the complete guided project. You can keep reading this free tutorial to work through the camera setup.

Make a Complete Card Battler in Godot e1788415191322 - How to Build a Godot Cinematic Camera for 3D Cutscenes
FREE GODOT COURSE
LEARN GODOT, UNITY, UNREAL & MORE
ACCESS FOR FREE
AVAILABLE FOR A LIMITED TIME ONLY

Build Your Godot Cinematic Camera

Creating the CinematicCamera Script

In the FileSystem dock, create a Scripts folder. Inside it, create cinematic_camera.gd. Extend Camera3D so you retain its existing functionality, and give the script the class name CinematicCamera:

class_name CinematicCamera
extends Camera3D

Save the script, select the existing Camera3D node in the main scene, and attach it.

Tracking a Target

Automatic tracking lets the camera rotate to keep looking at a subject, such as a car moving toward it. Start with an exported reference to the target:

@export var track_target : Node3D

In _process, check that a target has been assigned, then call look_at with its global position:

func _process (delta : float):
	if track_target != null:
		look_at(track_target.global_position)

The check skips the tracking call when track_target is null. To test it, drag a streetlight such as Streetlight3 into the camera’s Track Target field and press Play. The camera should face the light.

Godot 3D viewport showing the camera frustum pointed at a streetlight in the night scene, illustrating the track target in action.

While the game runs, return to Godot and switch the Scene panel to Remote. Find Streetlight3 among the live nodes and move it. Return to the game view to check that the camera keeps facing the light as its position changes.

Adding a Handheld Feel

For a subtle handheld effect, use the camera’s h_offset and v_offset properties. Adjusting them shifts the camera view horizontally and vertically while leaving the node’s position unchanged.

Godot Inspector panel showing the CinematicCamera with Track Target set to Streetlight3 and the H Offset and V Offset properties under Camera3D.

You’ll drive these offsets with noise so the movement varies smoothly over time. Add these exported variables:

@export var do_handheld : bool
@export var handheld_noise : FastNoiseLite
@export var handheld_amplitude : float
@export var handheld_frequency : float
  • do_handheld toggles the effect.
  • handheld_noise holds the FastNoiseLite resource used to generate it.
  • handheld_amplitude controls the size of the offset.
  • handheld_frequency controls how quickly the effect moves.

Understanding the FastNoiseLite Resource

Save and select the camera. In its Handheld Noise field, create a new FastNoiseLite resource, then expand it to see the noise preview.

Inspector preview of a FastNoiseLite resource showing a grayscale noise texture used to drive the handheld camera shake.

Think of the preview as a two-dimensional graph. As you sample changing positions across it, its smooth light and dark variations produce a value that rises and falls. That changing value will drive the camera offsets.

Leave the noise resource’s default properties in place. The camera’s amplitude and frequency settings will control the effect: an amplitude of 0.01 gives a subtle result, while 0.1 produces much larger movement. A higher frequency makes the shake faster.

Applying the Handheld Offset in Code

After the tracking code in _process, add an early return so the remaining logic runs only when handheld movement is enabled:

if not do_handheld:
	return

Add a script-level variable to keep track of time:

var time : float

Inside _process, after the early return, increase it by delta:

time += delta

Multiply time by the handheld frequency to calculate the position you’ll sample in the noise:

var offset : float = time * handheld_frequency

For example, a frequency of 100 makes the sampling offset advance by 100 each second. To begin with horizontal movement, sample along X while keeping Y at zero:

h_offset = handheld_noise.get_noise_2d(offset, 0)

With Do Handheld enabled, this unscaled sample can produce excessive movement. Multiply it by handheld_amplitude to keep the offset small:

h_offset = handheld_noise.get_noise_2d(offset, 0) * handheld_amplitude

Use an amplitude such as 0.01 and run the scene. Watch an object near the edge of the frame to see the horizontal movement.

Godot game preview running the scene with the handheld effect enabled, showing the orange-lit street as the camera shakes.

For vertical movement, sample along Y instead, keeping X at zero:

v_offset = handheld_noise.get_noise_2d(0, offset) * handheld_amplitude

Save and run again to see the two-axis effect. If 0.01 is hard to notice, try 0.02 temporarily, then return to the subtler value.

The CinematicCamera Script So Far

With tracking and handheld movement combined, the script looks like this:

class_name CinematicCamera
extends Camera3D

@export var track_target : Node3D

@export var do_handheld : bool
@export var handheld_noise : FastNoiseLite
@export var handheld_amplitude : float
@export var handheld_frequency : float

var time : float

func _process (delta : float):
	if track_target != null:
		look_at(track_target.global_position)

	if not do_handheld:
		return

	time += delta

	var offset : float = time * handheld_frequency
	h_offset = handheld_noise.get_noise_2d(offset, 0) * handheld_amplitude
	v_offset = handheld_noise.get_noise_2d(0, offset) * handheld_amplitude

You now have the camera behavior in place. Next, you’ll use camera attributes to adjust how the shot looks.

Adjust Exposure and Depth of Field

Adding a Camera Attributes Resource

Select the camera and find Attributes in the Inspector. Click the empty resource slot to see the available types:

Inspector dropdown showing the two Camera Attributes resource types: CameraAttributesPhysical and CameraAttributesPractical.

  • CameraAttributesPhysical uses real-world camera units, such as focal length.
  • CameraAttributesPractical offers simpler controls for adjusting the image.

Choose CameraAttributesPractical and expand it. You’ll see groups for depth of field blur, exposure, and auto exposure.

Exposure

With the camera selected, enable Preview in the 3D viewport so you can see the shot while changing settings.

Godot Inspector showing the CameraAttributesPractical resource with the Exposure section expanded, alongside the Camera Preview enabled in the 3D viewport.

Under Exposure, increasing Multiplier brightens the image; lowering it makes the image darker. Try the control to see its effect, then leave the multiplier at its default for this shot.

Depth of Field Blur

Reposition the camera so a traffic light appears in the foreground. You can leave Preview to move the camera in the regular viewport, or enable Pilot inside Preview and hold the right mouse button while moving with WASD.

To soften the scene’s overwhelming light glare, select the traffic light’s OmniLight3D and lower its Volumetric Fog Energy.

Return to the camera attributes and open DOF Blur. The two options affect different parts of the shot:

  • Far Enabled blurs distant objects.
  • Near Enabled blurs objects close to the camera.

Enable Near to soften the foreground traffic light while keeping the street behind it sharp.

Camera preview with the foreground traffic light blurred out after enabling Near depth of field blur in the Inspector.

Use the remaining controls to shape the effect:

  • Near Distance: objects closer than this threshold receive full blur. Increase it to extend that region farther into the scene, or lower it to bring the blur closer to the camera.
  • Near Transition: controls the fade from full blur to no blur. Larger values give a gentler falloff; try 5 to see the difference.
  • Amount: controls blur strength. Try around 0.1, then about 0.2 to make the effect more visible.

To compare the opposite effect, disable Near and enable Far. Reduce Far Distance and try a Far Transition of 20 to soften the background. Return to Near blur for the foreground light in this shot.

Focal Length and Field of View

Use the camera’s Field of View setting to refine the framing. With Preview enabled, try a narrower value such as 18 and watch how the view changes.

In the camera’s Transform, set its X position to 0 to center it on the road, then move it back slightly to place the streetlight in the shot. Adjust the near blur for the new framing by increasing Near Distance and setting Near Transition to around 3.

Before running the scene, turn Preview off and clear Track Target so the camera holds this composition instead of rotating toward a target.

Running game window showing the cinematic street view with a narrow field of view and the foreground street light blurred by near depth of field.

The shot now combines a narrower field of view with a softened foreground. Next, you’ll prepare additional viewpoints and a way to select them.

Switch Between Cutscene Cameras

Godot editor with a single Camera3D selected and a camera preview of the nighttime street scene visible in the 3D viewport.

The Problem With Multiple Cameras

Duplicate the camera and move the copy elsewhere in the scene. With two cameras available, you need to control which one supplies the active view.

Godot editor showing a second Camera3D placed in the scene with its gizmo visible and CinematicCamera properties open in the Inspector.

Relying on the cameras’ order in the Scene tree makes that choice fragile: rearranging the nodes can change the initial viewpoint. Use the camera’s own activation setting instead.

Using the Current Property

Select the camera you want to use, find Current in its Camera3D settings, and enable it. This marks it as the active camera. Setting another camera’s current property to true at runtime switches the view to that camera.

Godot Inspector panel with the Camera3D Current property toggle switched on, marking the selected camera as the active one.

The manager you’ll build next uses this property to perform each camera cut.

Rather than adding a separate animation track to toggle each camera, you can give a manager the available camera references and expose one function that selects a camera by ID.

Use an exported array instead of requiring every camera to be a child of the manager. That keeps the setup flexible for cameras that will later sit under moving objects, such as a car.

Creating the CameraManager Node and Script

Add a plain Node to the scene and name it CameraManager. Attach a new script named camera_manager.gd:

class_name CameraManager
extends Node

# Array of CinematicCamera nodes that can be managed
@export var cameras : Array[CinematicCamera]

The exported array lets you assign camera nodes from the Inspector, even when they occupy different places in the scene hierarchy.

Identifying Cameras With an ID

Open cinematic_camera.gd and add an exported string to identify each camera:

# Unique identifier for this camera instance
@export var id : String

Give each instance a unique ID. The manager will use that value to find the camera you want to activate.

The activate_camera Function

In camera_manager.gd, add activate_camera. It loops through the array, compares each ID with the requested value, and makes the matching camera current:

class_name CameraManager
extends Node

# Array of CinematicCamera nodes that can be managed
@export var cameras : Array[CinematicCamera]

# Activates the CinematicCamera with the matching camera_id
func activate_camera (camera_id : String):
	for cam in cameras:
		if cam.id == camera_id:
			cam.current = true

You don’t need a separate assignment to turn the previous camera off. Making the matching camera current transfers the active view to it.

Assigning the Cameras in the Inspector

Select CameraManager, add two elements to its Cameras array, and drag the two camera nodes into the slots.

Rename the camera looking down the street to intro and set its Id to intro. Rename the second camera to tracking, give it the ID tracking, and position and rotate it to face down the street, ready for the planned car shot.

Godot editor with the tracking CinematicCamera positioned in the scene and its camera preview visible in the Inspector on the right.

Use the camera preview to refine the framing. Vehicle animation and the timing of camera cuts come later; this setup gives you the camera controls and manager they will use.

Put Your Cinematic Camera Setup to Work

Your street scene now has a reusable cinematic camera script, shot controls, and a central way to switch viewpoints. You’ve covered how to:

  • Track a Node3D target with look_at.
  • Drive horizontal and vertical handheld offsets with FastNoiseLite.
  • Adjust exposure, near and far depth of field, and field of view.
  • Assign camera IDs and select a viewpoint through CameraManager.

With those pieces in place, you’re ready to continue the cutscene project and refine the shots as the vehicles are animated.

Continue with the Godot 4 Game Development Mini-Degree for the full course project and a structured path through Godot game development. Build on this camera setup as you keep developing your skills.

Did you come across any errors in this tutorial? Please let us know by completing this form and we’ll look into it!

FREE COURSES
Python Blog Image - How to Build a Godot Cinematic Camera for 3D Cutscenes

FINAL DAYS: Unlock coding courses in Unity, Godot, Unreal, Python and more.