diff --git a/src/AcDream.App/Rendering/FlyCamera.cs b/src/AcDream.App/Rendering/FlyCamera.cs
new file mode 100644
index 0000000..9dcd2a5
--- /dev/null
+++ b/src/AcDream.App/Rendering/FlyCamera.cs
@@ -0,0 +1,71 @@
+// src/AcDream.App/Rendering/FlyCamera.cs
+using System.Numerics;
+
+namespace AcDream.App.Rendering;
+
+public sealed class FlyCamera : ICamera
+{
+ public Vector3 Position { get; set; } = new(96, 96, 150);
+ public float Yaw { get; set; } = MathF.PI / 2f; // facing +Y
+ public float Pitch { get; set; } = -0.3f; // looking slightly down
+ public float FovY { get; set; } = MathF.PI / 3f;
+ public float Aspect { get; set; } = 16f / 9f;
+
+ public float MoveSpeed { get; set; } = 100f; // world units per second
+ public float MouseSensitivity { get; set; } = 0.003f;
+
+ private const float PitchLimit = 1.5533f; // ~89 degrees
+
+ public Matrix4x4 View
+ {
+ get
+ {
+ var forward = Forward();
+ return Matrix4x4.CreateLookAt(Position, Position + forward, Vector3.UnitZ);
+ }
+ }
+
+ public Matrix4x4 Projection
+ => Matrix4x4.CreatePerspectiveFieldOfView(FovY, Aspect, 1f, 5000f);
+
+ ///
+ /// Integrate position for one frame based on WASD + vertical keys.
+ /// W/S move forward/back in the horizontal plane (ignoring pitch).
+ /// A/D strafe left/right. Up/down translate along world Z.
+ ///
+ public void Update(double dt, bool w, bool a, bool s, bool d, bool up, bool down)
+ {
+ float step = (float)(MoveSpeed * dt);
+
+ // Forward in the horizontal plane (ignore pitch so W doesn't dive into ground).
+ var flatForward = new Vector3(MathF.Cos(Yaw), MathF.Sin(Yaw), 0f);
+ var right = new Vector3(MathF.Sin(Yaw), -MathF.Cos(Yaw), 0f);
+
+ if (w) Position += flatForward * step;
+ if (s) Position -= flatForward * step;
+ if (a) Position -= right * step;
+ if (d) Position += right * step;
+ if (up) Position += Vector3.UnitZ * step;
+ if (down) Position -= Vector3.UnitZ * step;
+ }
+
+ ///
+ /// Apply accumulated mouse deltas (pixels since last frame). Positive deltaX
+ /// rotates the view to the right (decreases yaw), positive deltaY rotates
+ /// down (decreases pitch).
+ ///
+ public void Look(float deltaX, float deltaY)
+ {
+ Yaw -= deltaX * MouseSensitivity;
+ Pitch = Math.Clamp(Pitch - deltaY * MouseSensitivity, -PitchLimit, PitchLimit);
+ }
+
+ private Vector3 Forward()
+ {
+ float cp = MathF.Cos(Pitch);
+ return new Vector3(
+ cp * MathF.Cos(Yaw),
+ cp * MathF.Sin(Yaw),
+ MathF.Sin(Pitch));
+ }
+}