From window.screen to devicePixelRatio: A Complete Guide to Browser Screen APIs#

While building a responsive layout debugger, I dove deep into browser screen APIs. Beyond the common screen.width, there’s a lot more to explore. Here’s what I learned.

Core API: The screen Object#

The window.screen object provides rich screen information:

interface ScreenInfo {
  width: number            // Physical screen width (pixels)
  height: number           // Physical screen height (pixels)
  availWidth: number       // Available width (minus taskbar)
  availHeight: number      // Available height (minus taskbar)
  colorDepth: number       // Color depth (usually 24 or 32-bit)
  pixelDepth: number       // Pixel depth (usually equals colorDepth)
  orientation: string      // Screen orientation (landscape-primary, etc.)
}

Get current screen info:

const screenInfo = {
  width: window.screen.width,
  height: window.screen.height,
  availWidth: window.screen.availWidth,
  availHeight: window.screen.availHeight,
  colorDepth: window.screen.colorDepth,
  orientation: window.screen.orientation?.type || 'unknown'
}

console.log(screenInfo)
// { width: 1920, height: 1080, availWidth: 1920, availHeight: 1040, colorDepth: 24 }

Note: In multi-monitor setups, screen.width returns the primary monitor resolution. If your window is on a secondary monitor, these values may be inaccurate.

Device Pixel Ratio: devicePixelRatio Matters#

With Retina displays and high-DPI monitors, CSS pixels and physical pixels are no longer 1:1. devicePixelRatio is that ratio:

const dpr = window.devicePixelRatio
// Regular screen: 1
// Retina display: 2 or 3
// 4K monitor: might be 1.5 or 2

// Calculate Canvas real resolution
const canvas = document.getElementById('myCanvas')
const rect = canvas.getBoundingClientRect()

// Set Canvas physical resolution
canvas.width = rect.width * dpr
canvas.height = rect.height * dpr

// Scale context to match CSS pixels
const ctx = canvas.getContext('2d')
ctx.scale(dpr, dpr)

This ratio directly affects Canvas clarity. Without handling devicePixelRatio, Canvas looks blurry on high-DPI screens.

Window Dimensions: innerHeight vs outerHeight#

Browser windows have two sets of dimensions:

// Window outer dimensions (includes title bar, borders)
window.outerWidth   // Entire browser window width
window.outerHeight  // Entire browser window height

// Window inner dimensions (viewport size)
window.innerWidth   // Viewport width (includes scrollbar)
window.innerHeight  // Viewport height (includes scrollbar)

// Document dimensions (actual page size)
document.documentElement.scrollWidth
document.documentElement.scrollHeight

The differences:

Property Meaning Typical Value (1920×1080 screen)
outerHeight Total browser window height 1040px
innerHeight Viewport height (with scrollbar) 900px
scrollHeight Full document height Can be large

Screen Orientation Detection: The orientation API#

Mobile development requires detecting portrait/landscape:

// Get current orientation
const orientation = window.screen.orientation?.type
// Possible values:
// - 'portrait-primary'
// - 'portrait-secondary' (upside down)
// - 'landscape-primary'
// - 'landscape-secondary' (upside down)

// Listen for orientation changes
window.screen.orientation?.addEventListener('change', (e) => {
  console.log('Screen orientation changed:', e.target.type)
})

// Traditional method (better compatibility)
window.addEventListener('orientationchange', () => {
  console.log('Orientation:', window.orientation)  // 0, 90, -90, 180
})

Touch Screen Detection#

Detect if a device supports touch:

const touchSupport = {
  // Method 1: Check events
  hasTouch: 'ontouchstart' in window,
  
  // Method 2: Check touch points
  maxTouchPoints: navigator.maxTouchPoints || 0,
  
  // Method 3: CSS media query
  isTouchDevice: window.matchMedia('(hover: none) and (pointer: coarse)').matches
}

console.log(touchSupport)
// PC: { hasTouch: false, maxTouchPoints: 0, isTouchDevice: false }
// iPad: { hasTouch: true, maxTouchPoints: 5, isTouchDevice: true }

Real-time Window Change Monitoring#

Building responsive tools requires real-time updates:

function useScreenInfo() {
  const [info, setInfo] = useState({
    width: window.innerWidth,
    height: window.innerHeight,
    dpr: window.devicePixelRatio
  })
  
  useEffect(() => {
    const updateInfo = () => {
      setInfo({
        width: window.innerWidth,
        height: window.innerHeight,
        dpr: window.devicePixelRatio
      })
    }
    
    window.addEventListener('resize', updateInfo)
    return () => window.removeEventListener('resize', updateInfo)
  }, [])
  
  return info
}

Practical Use Cases#

1. Responsive Breakpoint Detection#

function getCurrentBreakpoint() {
  const width = window.innerWidth
  
  if (width < 640) return 'xs'      // Mobile
  if (width < 768) return 'sm'      // Large mobile
  if (width < 1024) return 'md'     // Tablet
  if (width < 1280) return 'lg'     // Small laptop
  if (width < 1536) return 'xl'     // Desktop
  return '2xl'                       // Large screen
}

2. HiDPI Canvas Rendering#

function setupHiDPICanvas(canvas) {
  const dpr = window.devicePixelRatio || 1
  const rect = canvas.getBoundingClientRect()
  
  // Set physical resolution
  canvas.width = rect.width * dpr
  canvas.height = rect.height * dpr
  
  // CSS size stays the same
  canvas.style.width = `${rect.width}px`
  canvas.style.height = `${rect.height}px`
  
  // Scale context
  const ctx = canvas.getContext('2d')
  ctx.scale(dpr, dpr)
  
  return ctx
}

3. Adaptive Layout#

// Adjust toolbar based on viewport height
function adjustToolbar() {
  const vh = window.innerHeight
  
  if (vh < 600) {
    // Small screen: compact toolbar
    toolbar.classList.add('compact')
  } else {
    // Large screen: expanded toolbar
    toolbar.classList.remove('compact')
  }
}

Caveats#

1. Privacy Restrictions#

For privacy, some browsers limit returned information:

// Firefox privacy mode
window.screen.width  // Might return 0 or inaccurate value

// Some browsers round to nearest 100
window.screen.width  // Actual 1920, returns 1900

2. Async Access#

Some info (like orientation) might need async access:

if (screen.orientation) {
  screen.orientation.addEventListener('change', () => {
    console.log('Orientation changed')
  })
}

3. Mobile Device Quirks#

Mobile devices return logical resolution, not physical:

// iPhone 12
screen.width    // 390 (logical resolution)
screen.height   // 844 (logical resolution)

// Physical resolution needs calculation
const physicalWidth = screen.width * devicePixelRatio
const physicalHeight = screen.height * devicePixelRatio
// 1170 × 2532

Summary#

Browser screen APIs may seem simple, but combining them enables powerful features:

  • Responsive debugging: Real-time viewport size and breakpoint display
  • HiDPI Canvas rendering: Adjust resolution based on devicePixelRatio
  • Device detection: Touch screen, orientation, available space
  • Layout optimization: Auto-adjust interface based on viewport height

Based on these APIs, I built an online tool: Screen Info Detector, showing all screen parameters in real-time for frontend developers debugging responsive layouts.


Related: Responsive Test Tool | Percentage Calculator