An interactive 3D projective geometry sandbox for learning how pinhole cameras and projection transformations work. Manipulate camera intrinsics and extrinsics in real-time, watch projection rays converge through a pinhole, and see the math come alive.
| 3D World View | Camera Output |
|---|---|
![]() |
![]() |
- Left viewport - Orbit the 3D scene freely. See the camera frustum, projection rays, and image plane as physical objects in space.
- Right viewport - Live output from the virtual pinhole camera, computed from the K and [R|t] matrices.
Drag sliders to adjust all 11 camera parameters and watch everything update in real-time:
Intrinsics (K matrix):
fx,fy- Focal lengths (horizontal/vertical field of view)cx,cy- Principal point (where optical axis meets the image)skew- Pixel axis non-orthogonality
Extrinsics ([R|t] matrix):
pitch,yaw,roll- Camera orientation (Euler angles)tx,ty,tz- Camera position in world
See the K, [R|t], and P = K[R|t] matrices update live as you drag sliders. Cells are color-coded by parameter type and highlight when the corresponding slider is active.
Toggle rays to see how 3D world points project through the pinhole onto the image plane. Each object gets a colored ray showing:
- The path from the 3D object to the camera center (pinhole)
- The continuation from the pinhole to the hit point on the image plane
- Select & Drag - Click objects in the 3D view to select them, drag to reposition
- Place Objects - Add cubes, spheres, or marker points to the scene
- Delete - Remove user-placed objects
- Hover Tooltip - See 3D coordinates and projected 2D pixel coordinates for any object
The camera's image plane is rendered as a floating textured quad in the 3D scene. The texture is a live render-to-texture pass from the virtual camera, so you can see exactly what the camera captures as a physical object in space.
The app implements the standard pinhole camera model from computer vision:
[ fx s cx ]
P = K[R|t] K = [ 0 fy cy ] [R|t] = 3x4 extrinsic matrix
[ 0 0 1 ]
- K maps 3D camera coordinates to 2D pixel coordinates
- [R|t] maps world coordinates to camera coordinates
- P is the full 3x4 projection matrix
The rotation matrix R is built from Euler angles: R = Rz(roll) * Ry(yaw) * Rx(pitch)
Camera center in world coordinates: C = -R^T * t
- Node.js 18+
- npm
git clone https://github.com/SdSadat/Finite-Camera-Simulator.git
cd Finite-Camera-Simulator
npm install
npm run devOpen http://localhost:5173 in your browser.
npm run build
npm run preview| Action | Where | Effect |
|---|---|---|
| Orbit / Pan / Zoom | Left viewport | Explore the 3D scene |
| Click object | Left viewport | Select (yellow outline) |
| Drag object | Left viewport | Move on XZ plane |
| Click | Right viewport | Cast backprojection ray |
| Double-click | Right viewport | Clear all backprojection rays |
| Key | Action |
|---|---|
1 |
Select tool |
2 |
Place cube |
3 |
Place sphere |
4 |
Place point |
Del |
Delete selected object |
Esc |
Return to select mode |
R |
Toggle projection rays |
I |
Toggle image plane |
M |
Toggle matrix display |
L |
Toggle labels |
User Input ──> Zustand Store <──> Math Engine (pure TS)
(sliders, (single source |
orbit, of truth) buildK(), buildRt(),
clicks) | projectToScreen(),
+----------+------+ backprojectRay()
| | |
v v v
Rendering UI Interaction
(Three.js) (DOM) (raycasting,
| drag, place)
v
Split Viewport
(orbit + virtual camera)
- TypeScript - Strict mode, ES2022
- Three.js - 3D rendering, orbit controls, scissor-based split viewport
- Zustand - Lightweight state management with selector subscriptions
- Vite - Build tooling and dev server
src/
├── main.ts # Entry point
├── app.ts # Orchestrator: wires store -> rendering -> UI
├── types/
│ ├── camera-model.ts # CameraIntrinsics, CameraExtrinsics, Mat3/3x4
│ ├── geometry.ts # Point3D, Point2D, Line3D, Plane3D
│ └── module.ts # IModule interfaces (for future guided lessons)
├── math/
│ ├── intrinsics.ts # buildK(), invertK()
│ ├── extrinsics.ts # eulerToRotationMatrix(), buildRt(), getCameraCenter()
│ ├── projection.ts # projectToScreen(), backprojectRay(), buildProjectionMatrix()
│ └── homogeneous.ts # Homogeneous coordinate utilities
├── rendering/
│ ├── split-viewport.ts # Dual viewport renderer (orbit + virtual camera)
│ ├── scene-manager.ts # Three.js scene setup, objects, lighting
│ ├── camera-rig.ts # Visual pinhole camera (frustum + image plane + axes)
│ ├── projection-rays.ts # Ray visualization from objects through pinhole
│ └── image-plane-mesh.ts # Render-to-texture floating image plane
├── interaction/
│ └── scene-interaction.ts # Selection, dragging, placement, backprojection
├── ui/
│ ├── slider-factory.ts # Camera parameter sliders with store binding
│ ├── matrix-display.ts # Live K, [R|t], P matrix panels
│ ├── toolbar.ts # Object placement toolbar
│ └── toggle-bar.ts # Visibility toggle buttons
└── styles/
└── global.css # Dark theme
MIT







