JavaScript Screen Orientation Table
| Piece | What it does | Field note |
|---|---|---|
screen.orientation.type | landscape-primary etc | The CURRENT orientation with its base side named |
screen.orientation.angle | Degrees rotated | 0/90/180/270 - the raw rotation the type abbreviates |
orientation.onchange | Rotate events | Fires on device rotation - rebuild charts, reflow canvases |
orientation.lock('landscape') | Pin the orientation | Fullscreen-only in practice - games and video players |
unlock() | Give control back | Always pair a lock with an unlock on exit |
CSS alternative first | orientation: media query | @media (orientation: landscape) handles LAYOUT - API is for STATE |
angle vs type drift | Base side matters | landscape-secondary = 270ยฐ - angle is ground truth for math |
Desktop: fixed value | No rotation event | Desktop browsers report a constant - feature-detect mobile use |
Screen Orientation reports device rotation: orientation.type names it (landscape-primary, portrait-secondary...) and orientation.angle gives the raw degrees, with onchange firing when the device rotates - the moment to rebuild canvases, charts and game layouts.
Bottom line: CSS @media (orientation: landscape) already handles LAYOUT rotation - the API exists for STATE logic CSS cannot express: canvas resizing, game input mapping, rotation-dependent calculations. orientation.lock() pins the orientation but works in practice only under fullscreen, and every lock pairs with unlock on exit.
The honest part: desktop browsers report a constant orientation and never fire onchange - the API is a mobile-device surface, and code should treat it as such (feature-detect the event, not just the object).
How to use
- Rebuild on rotation: screen.orientation.onchange = () => resizeCanvasTo(window.innerWidth) - charts and game surfaces re-fit after the device turns.
- Pin a game landscape: await screen.orientation.lock('landscape') after requestFullscreen() - and unlock() when leaving the game screen.
- Read the angle for math: orientation.angle (0/90/180/270) is the ground truth - type names are for humans, angle is for calculations.
Frequently asked questions
What is the difference between orientation.type and orientation.angle?
Two views of one rotation. type is the semantic name: portrait-primary, landscape-secondary - where 'primary' is the device's natural orientation and 'secondary' is its flip. angle is the raw degrees the screen content is rotated relative to natural: 0, 90, 180, 270. They agree, but the mapping depends on the device's natural orientation (a phone whose primary is portrait has landscape at 90; a natural-landscape tablet has it at 0 or 180). For display and logging, type reads better; for math - canvas transforms, coordinate mapping, accelerometer cross-referencing - angle is the ground truth, because arithmetic on names is guesswork.
Why does orientation.lock() only work under fullscreen?
User control policy. Spontaneously locking a page into landscape while the user holds the phone in portrait is hostile - so the spec ties orientation.lock to fullscreen (or installed-PWA display modes): the user entered a mode where landscape IS the intent (a game started, a video playing), and the lock expresses that intent. Outside fullscreen the call rejects. The pattern: requestFullscreen() then await screen.orientation.lock('landscape') on game start; on exit, unlock() before exiting fullscreen. Always pair: a lock without an unlock trap-holes the next visitor of the page in the wrong orientation.
When do I need the orientation API versus the CSS media query?
Layout versus state. @media (orientation: landscape) reflows LAYOUT declaratively: grids re-column, sidebars appear - CSS handles everything that is styling. The JavaScript API handles STATE logic that CSS cannot express: re-rendering a canvas at the new dimensions, remapping game controls (accelerometer axes swap with rotation), pausing/resuming orientation-sensitive logic, analytics on how users rotate. If the response is pure styling, the media query is the answer and the API adds nothing; if the response is code - redrawing, recalculating, remapping - the API's onchange is the hook. Both fire on the same rotation; they serve different halves.
Why doesn't my desktop code ever see an orientation change?
Desktop monitors do not rotate (usually), so desktop browsers report a constant type and angle and never fire onchange - the API is a mobile-device surface by physics, not by specification. The engineering consequences: feature-detect the EVENT-ability for rotation logic (or just accept it never fires on desktop), never gate core layout on orientation API state (CSS media queries handle desktop fine), and treat orientation-specific behavior as a mobile enhancement. Testing rotation requires device emulation in devtools or a real device - which is also why rotation bugs ship: the desktop development loop never exercises the path.