Back to Blog

Window Management API: Complete Technical Deep Dive

April 24, 2026107 min read0 views

Window Management API: Complete Technical Deep Dive

The Window Management API enables web applications to enumerate connected displays and position windows across them. Understanding its internals reveals how browsers provide multi-monitor awareness while maintaining the security boundaries that protect user privacy.

Why Window Management API Exists

Before this API, web apps were blind to multi-monitor setups:

┌─────────────────────────────────────────────────────────────────────┐
│                 Pre-Window Management API Era                       │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  Problems Web Applications Faced:                                   │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │                                                              │   │
│  │  1. No Multi-Monitor Awareness                               │   │
│  │     • window.screen only showed current monitor              │   │
│  │     • No way to know how many monitors existed               │   │
│  │     • Couldn't detect monitor arrangement                    │   │
│  │                                                              │   │
│  │  2. Limited Window Positioning                                │   │
│  │     • window.open() could only position on current screen    │   │
│  │     • Couldn't request specific monitor for new windows      │   │
│  │     • moveTo() clamped to current screen bounds              │   │
│  │                                                              │   │
│  │  3. Poor Multi-Monitor UX                                     │   │
│  │     • Presentation apps couldn't show slides on projector    │   │
│  │     • Trading apps couldn't span multiple monitors           │   │
│  │     • Video editors couldn't have timeline on second screen  │   │
│  │     • Dialogs would open on wrong monitor                    │   │
│  │                                                              │   │
│  │  4. Fullscreen Limitations                                    │   │
│  │     • requestFullscreen() only worked on current monitor     │   │
│  │     • No way to fullscreen to secondary display              │   │
│  │     • Presentation mode was severely limited                 │   │
│  │                                                              │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
│  Workarounds (All Hacky/Incomplete):                                │
│  • Open window with extreme coordinates hoping it lands elsewhere  │
│  • Ask user to manually drag windows to desired monitor            │
│  • Use screen.availWidth as proxy (unreliable)                     │
│  • Native app wrappers (Electron) for multi-monitor support        │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────┐
│                 Window Management API Solution                      │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  Complete Multi-Monitor Control                                     │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │                                                              │   │
│  │   ┌─────────────────┐   ┌─────────────────┐                 │   │
│  │   │   Monitor 1     │   │   Monitor 2     │                 │   │
│  │   │   (Primary)     │   │   (Secondary)   │                 │   │
│  │   │                 │   │                 │                 │   │
│  │   │  ┌───────────┐  │   │  ┌───────────┐  │                 │   │
│  │   │  │ Main App  │  │   │  │ Slideshow │  │                 │   │
│  │   │  │ Controls  │  │   │  │ Fullscreen│  │                 │   │
│  │   │  └───────────┘  │   │  └───────────┘  │                 │   │
│  │   │                 │   │                 │                 │   │
│  │   └─────────────────┘   └─────────────────┘                 │   │
│  │                                                              │   │
│  │  API Capabilities:                                           │   │
│  │  • Enumerate all connected displays                         │   │
│  │  • Get screen geometry (position, size, color depth)        │   │
│  │  • Position windows on specific monitors                    │   │
│  │  • Request fullscreen on any monitor                        │   │
│  │  • Detect display changes (connect/disconnect)              │   │
│  │  • Get internal vs external display info                    │   │
│  │                                                              │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
│  Use Cases Enabled:                                                 │
│  • Presentation mode (notes on laptop, slides on projector)        │
│  • Trading platforms (multiple windows across monitors)            │
│  • Video editing (preview on external, timeline on primary)        │
│  • Photo culling (full image on color-accurate external)           │
│  • Productivity apps (tools on one screen, canvas on another)      │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

Architecture Deep Dive

Browser Internal Architecture

┌─────────────────────────────────────────────────────────────────────────────┐
│              Window Management Internal Architecture                         │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  Web Application (Renderer Process)                                          │
│  ┌────────────────────────────────────────────────────────────────────┐     │
│  │  JavaScript Context                                                 │     │
│  │  ┌─────────────────────────────────────────────────────────────┐   │     │
│  │  │  const screens = await window.getScreenDetails();            │   │     │
│  │  │  const externalScreen = screens.screens.find(s => !s.isPrimary);│  │     │
│  │  │  window.open(url, '_blank', `left=${externalScreen.left}`)   │   │     │
│  │  └─────────────────────────────────────────────────────────────┘   │     │
│  │                              │                                      │     │
│  │                              ▼                                      │     │
│  │  ┌─────────────────────────────────────────────────────────────┐   │     │
│  │  │  Window Management Interface                                 │   │     │
│  │  │  • Check permission state                                   │   │     │
│  │  │  • Validate secure context                                  │   │     │
│  │  │  • Send IPC to Browser Process                              │   │     │
│  │  └─────────────────────────────────────────────────────────────┘   │     │
│  └────────────────────────────────────────────────────────────────────┘     │
│                              │                                               │
│                              │ IPC (Mojo)                                    │
│                              ▼                                               │
│  Browser Process (Privileged)                                                │
│  ┌────────────────────────────────────────────────────────────────────┐     │
│  │                                                                     │     │
│  │  ┌─────────────────────┐     ┌─────────────────────────────────┐  │     │
│  │  │ Permission Service  │     │ Screen Enumeration Service       │  │     │
│  │  │ • Check granted     │────▶│ • Query display configuration    │  │     │
│  │  │ • Show prompt if    │     │ • Calculate virtual screen space │  │     │
│  │  │   needed            │     │ • Monitor for changes            │  │     │
│  │  │ • Persist decision  │     │ • Filter based on permission     │  │     │
│  │  └─────────────────────┘     └─────────────────────────────────┘  │     │
│  │                                          │                         │     │
│  │                                          ▼                         │     │
│  │  ┌─────────────────────────────────────────────────────────────┐  │     │
│  │  │ Permission Prompt (if needed)                                │  │     │
│  │  │ ┌───────────────────────────────────────────────────────┐   │  │     │
│  │  │ │ "example.com wants to know about your screens"        │   │  │     │
│  │  │ │                                                       │   │  │     │
│  │  │ │ This allows the site to:                              │   │  │     │
│  │  │ │ • See how many screens you have                       │   │  │     │
│  │  │ │ • Place windows on different screens                  │   │  │     │
│  │  │ │                                                       │   │  │     │
│  │  │ │              [Block]  [Allow]                         │   │  │     │
│  │  │ └───────────────────────────────────────────────────────┘   │  │     │
│  │  └─────────────────────────────────────────────────────────────┘  │     │
│  │                                          │                         │     │
│  │                                          │ Permission granted      │     │
│  │                                          ▼                         │     │
│  │  ┌─────────────────────────────────────────────────────────────┐  │     │
│  │  │ Platform Display APIs                                        │  │     │
│  │  │ ┌───────────────┬───────────────┬──────────────────────┐   │  │     │
│  │  │ │ Windows       │ macOS         │ Linux                │   │  │     │
│  │  │ │ EnumDisplay   │ NSScreen      │ X11/Wayland          │   │  │     │
│  │  │ │ Monitors()    │ screens       │ display APIs         │   │  │     │
│  │  │ │ DEVMODE       │ deviceDesc.   │ xrandr/wl_output     │   │  │     │
│  │  │ └───────────────┴───────────────┴──────────────────────┘   │  │     │
│  │  └─────────────────────────────────────────────────────────────┘  │     │
│  │                                          │                         │     │
│  └──────────────────────────────────────────┼─────────────────────────┘     │
│                                             │                               │
│                                             ▼                               │
│  ScreenDetails Object Returned to Renderer                                   │
│  ┌────────────────────────────────────────────────────────────────────┐     │
│  │  {                                                                  │     │
│  │    screens: [                                                       │     │
│  │      {                                                              │     │
│  │        availLeft: 0, availTop: 0,                                  │     │
│  │        availWidth: 1920, availHeight: 1040,                        │     │
│  │        left: 0, top: 0, width: 1920, height: 1080,                │     │
│  │        colorDepth: 24, pixelDepth: 24,                            │     │
│  │        devicePixelRatio: 1, isInternal: true, isPrimary: true,    │     │
│  │        label: "Built-in Retina Display"                            │     │
│  │      },                                                             │     │
│  │      { ... secondary screen ... }                                   │     │
│  │    ],                                                               │     │
│  │    currentScreen: { ... }                                           │     │
│  │  }                                                                  │     │
│  └────────────────────────────────────────────────────────────────────┘     │
│                                                                              │
└──────────────────────────────────────────────────────────────────────────────┘

Screen Coordinate System

┌─────────────────────────────────────────────────────────────────────┐
│                    Virtual Screen Space                             │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  Multiple monitors form a virtual coordinate space:                 │
│                                                                     │
│       (-1920, 0)        (0, 0)          (1920, 0)                   │
│            │               │                │                       │
│            ▼               ▼                ▼                       │
│    ┌───────────────┬───────────────┬───────────────┐               │
│    │               │               │               │               │
│    │   Monitor 1   │   Monitor 2   │   Monitor 3   │               │
│    │   1920x1080   │   1920x1080   │   1920x1080   │               │
│    │   (Left)      │   (Primary)   │   (Right)     │               │
│    │               │               │               │               │
│    └───────────────┴───────────────┴───────────────┘               │
│                                                                     │
│  Common Arrangements:                                               │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │                                                              │   │
│  │  Side-by-Side:     Stacked:         L-Shaped:               │   │
│  │  ┌────┬────┐      ┌────┐           ┌────┐                   │   │
│  │  │ 1  │ 2  │      │ 1  │           │ 1  ├────┐              │   │
│  │  └────┴────┘      ├────┤           └────┤ 2  │              │   │
│  │                   │ 2  │                └────┘              │   │
│  │                   └────┘                                     │   │
│  │                                                              │   │
│  │  Note: Origin (0,0) is typically top-left of primary        │   │
│  │  Monitors to the left have negative X coordinates           │   │
│  │  Monitors above have negative Y coordinates                 │   │
│  │                                                              │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
│  Screen Properties Explained:                                       │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │                                                              │   │
│  │  left, top:        Position in virtual space                │   │
│  │  width, height:    Total screen dimensions                  │   │
│  │                                                              │   │
│  │  availLeft, availTop:    Usable area (excludes taskbar)     │   │
│  │  availWidth, availHeight: Usable dimensions                 │   │
│  │                                                              │   │
│  │  devicePixelRatio: Physical pixels per CSS pixel            │   │
│  │                    (2 for Retina/HiDPI displays)            │   │
│  │                                                              │   │
│  │  isInternal:       Built-in display (laptop screen)         │   │
│  │  isPrimary:        Primary/main display                     │   │
│  │  label:            User-friendly display name               │   │
│  │                                                              │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

Complete API Implementation

Screen Manager

// window-management.ts

interface ScreenInfo {
  availLeft: number;
  availTop: number;
  availWidth: number;
  availHeight: number;
  left: number;
  top: number;
  width: number;
  height: number;
  colorDepth: number;
  pixelDepth: number;
  devicePixelRatio: number;
  isInternal: boolean;
  isPrimary: boolean;
  label: string;
}

interface ScreenDetails {
  screens: ScreenInfo[];
  currentScreen: ScreenInfo;
}

class WindowManagementManager {
  private screenDetails: ScreenDetails | null = null;
  private listeners = new Set<(screens: ScreenInfo[]) => void>();
  private changeListener: ((event: Event) => void) | null = null;

  static isSupported(): boolean {
    return 'getScreenDetails' in window;
  }

  async requestPermission(): Promise<PermissionState> {
    if (!WindowManagementManager.isSupported()) {
      return 'denied';
    }

    try {
      // Requesting screen details implicitly requests permission
      const details = await (window as any).getScreenDetails();
      this.screenDetails = details;
      this.setupChangeListener(details);
      return 'granted';
    } catch (error) {
      if (error instanceof DOMException) {
        if (error.name === 'NotAllowedError') {
          return 'denied';
        }
      }
      throw error;
    }
  }

  async getScreenDetails(): Promise<ScreenDetails> {
    if (!WindowManagementManager.isSupported()) {
      // Fallback to basic screen info
      return this.getFallbackScreenDetails();
    }

    if (this.screenDetails) {
      return this.screenDetails;
    }

    try {
      this.screenDetails = await (window as any).getScreenDetails();
      this.setupChangeListener(this.screenDetails);
      return this.screenDetails;
    } catch (error) {
      console.warn('Failed to get screen details, using fallback');
      return this.getFallbackScreenDetails();
    }
  }

  private getFallbackScreenDetails(): ScreenDetails {
    // Use basic window.screen for single-monitor fallback
    const screen: ScreenInfo = {
      availLeft: window.screen.availLeft || 0,
      availTop: window.screen.availTop || 0,
      availWidth: window.screen.availWidth,
      availHeight: window.screen.availHeight,
      left: 0,
      top: 0,
      width: window.screen.width,
      height: window.screen.height,
      colorDepth: window.screen.colorDepth,
      pixelDepth: window.screen.pixelDepth,
      devicePixelRatio: window.devicePixelRatio,
      isInternal: true,
      isPrimary: true,
      label: 'Primary Display'
    };

    return {
      screens: [screen],
      currentScreen: screen
    };
  }

  private setupChangeListener(details: any): void {
    if (this.changeListener) return;

    this.changeListener = () => {
      // Re-fetch screen details on change
      this.refreshScreenDetails();
    };

    details.addEventListener('screenschange', this.changeListener);
  }

  private async refreshScreenDetails(): Promise<void> {
    try {
      this.screenDetails = await (window as any).getScreenDetails();
      this.notifyListeners();
    } catch (error) {
      console.error('Failed to refresh screen details:', error);
    }
  }

  // Subscribe to screen changes
  onScreensChange(callback: (screens: ScreenInfo[]) => void): () => void {
    this.listeners.add(callback);
    return () => this.listeners.delete(callback);
  }

  private notifyListeners(): void {
    const screens = this.screenDetails?.screens || [];
    this.listeners.forEach(listener => listener(screens));
  }

  // Utility methods
  getPrimaryScreen(): ScreenInfo | null {
    return this.screenDetails?.screens.find(s => s.isPrimary) || null;
  }

  getExternalScreens(): ScreenInfo[] {
    return this.screenDetails?.screens.filter(s => !s.isInternal) || [];
  }

  getCurrentScreen(): ScreenInfo | null {
    return this.screenDetails?.currentScreen || null;
  }

  getScreenCount(): number {
    return this.screenDetails?.screens.length || 1;
  }

  isMultiScreen(): boolean {
    return this.getScreenCount() > 1;
  }

  // Find screen by position
  getScreenAtPoint(x: number, y: number): ScreenInfo | null {
    if (!this.screenDetails) return null;

    return this.screenDetails.screens.find(screen =>
      x >= screen.left &&
      x < screen.left + screen.width &&
      y >= screen.top &&
      y < screen.top + screen.height
    ) || null;
  }

  // Get screen by label (for persistence)
  getScreenByLabel(label: string): ScreenInfo | null {
    return this.screenDetails?.screens.find(s => s.label === label) || null;
  }
}

Window Placement Utilities

// window-placement.ts

interface WindowPlacementOptions {
  screen?: ScreenInfo;
  position?: 'center' | 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight';
  width?: number;
  height?: number;
  fullscreen?: boolean;
}

class WindowPlacement {
  private screenManager = new WindowManagementManager();

  async initialize(): Promise<void> {
    await this.screenManager.requestPermission();
  }

  // Open window on specific screen
  async openOnScreen(
    url: string,
    options: WindowPlacementOptions = {}
  ): Promise<Window | null> {
    const targetScreen = options.screen ||
      await this.getBestScreen(options);

    const width = options.width || 800;
    const height = options.height || 600;

    const { left, top } = this.calculatePosition(
      targetScreen,
      width,
      height,
      options.position || 'center'
    );

    const features = [
      `left=${left}`,
      `top=${top}`,
      `width=${width}`,
      `height=${height}`
    ].join(',');

    const newWindow = window.open(url, '_blank', features);

    // Request fullscreen if specified
    if (options.fullscreen && newWindow) {
      // Need to wait for window to load
      newWindow.addEventListener('load', () => {
        this.makeFullscreen(newWindow, targetScreen);
      });
    }

    return newWindow;
  }

  private async getBestScreen(options: WindowPlacementOptions): Promise<ScreenInfo> {
    const details = await this.screenManager.getScreenDetails();

    // Default to primary screen
    return details.screens.find(s => s.isPrimary) || details.screens[0];
  }

  private calculatePosition(
    screen: ScreenInfo,
    width: number,
    height: number,
    position: WindowPlacementOptions['position']
  ): { left: number; top: number } {
    const availLeft = screen.availLeft;
    const availTop = screen.availTop;
    const availWidth = screen.availWidth;
    const availHeight = screen.availHeight;

    switch (position) {
      case 'topLeft':
        return { left: availLeft, top: availTop };

      case 'topRight':
        return { left: availLeft + availWidth - width, top: availTop };

      case 'bottomLeft':
        return { left: availLeft, top: availTop + availHeight - height };

      case 'bottomRight':
        return {
          left: availLeft + availWidth - width,
          top: availTop + availHeight - height
        };

      case 'center':
      default:
        return {
          left: availLeft + (availWidth - width) / 2,
          top: availTop + (availHeight - height) / 2
        };
    }
  }

  // Request fullscreen on specific screen
  async makeFullscreen(
    target: Window | Element,
    screen?: ScreenInfo
  ): Promise<void> {
    const element = target instanceof Window
      ? target.document.documentElement
      : target;

    const fullscreenOptions: FullscreenOptions & { screen?: ScreenInfo } = {};

    if (screen) {
      fullscreenOptions.screen = screen;
    }

    try {
      await element.requestFullscreen(fullscreenOptions);
    } catch (error) {
      console.error('Fullscreen request failed:', error);
    }
  }

  // Move existing window to screen
  async moveWindowToScreen(
    targetWindow: Window,
    screen: ScreenInfo,
    options: { position?: WindowPlacementOptions['position'] } = {}
  ): Promise<void> {
    const width = targetWindow.outerWidth;
    const height = targetWindow.outerHeight;

    const { left, top } = this.calculatePosition(
      screen,
      width,
      height,
      options.position || 'center'
    );

    targetWindow.moveTo(left, top);
  }

  // Tile windows across screens
  async tileWindows(
    windows: Window[],
    screens?: ScreenInfo[]
  ): Promise<void> {
    const availableScreens = screens ||
      (await this.screenManager.getScreenDetails()).screens;

    windows.forEach((win, index) => {
      const screen = availableScreens[index % availableScreens.length];
      const { left, top } = this.calculatePosition(
        screen,
        screen.availWidth,
        screen.availHeight,
        'topLeft'
      );

      win.moveTo(left, top);
      win.resizeTo(screen.availWidth, screen.availHeight);
    });
  }
}

Real-World Production Patterns

1. Presentation Mode

// presentation-mode.ts

interface Slide {
  id: string;
  content: string;
  notes?: string;
}

class PresentationMode {
  private screenManager = new WindowManagementManager();
  private presenterWindow: Window | null = null;
  private slideshowWindow: Window | null = null;
  private slides: Slide[] = [];
  private currentSlideIndex = 0;

  async initialize(slides: Slide[]): Promise<void> {
    this.slides = slides;
    await this.screenManager.requestPermission();
  }

  async startPresentation(): Promise<void> {
    const details = await this.screenManager.getScreenDetails();

    if (details.screens.length < 2) {
      // Single monitor - show slideshow only
      await this.startSingleMonitorMode();
      return;
    }

    // Multi-monitor - presenter view on primary, slideshow on external
    await this.startMultiMonitorMode(details);
  }

  private async startSingleMonitorMode(): Promise<void> {
    // Open slideshow in current window, fullscreen
    this.renderSlideshow(document.body);
    await document.documentElement.requestFullscreen();

    // Show presenter notes in a small overlay
    this.addNotesOverlay();
  }

  private async startMultiMonitorMode(details: ScreenDetails): Promise<void> {
    const primaryScreen = details.screens.find(s => s.isPrimary)!;
    const externalScreen = details.screens.find(s => !s.isPrimary)!;

    // Open slideshow on external screen
    const slideshowUrl = this.createSlideshowDataUrl();
    this.slideshowWindow = window.open(
      slideshowUrl,
      'slideshow',
      this.getWindowFeatures(externalScreen, true)
    );

    // Wait for window to load, then fullscreen
    if (this.slideshowWindow) {
      this.slideshowWindow.addEventListener('load', async () => {
        await this.slideshowWindow!.document.documentElement.requestFullscreen({
          screen: externalScreen
        } as FullscreenOptions);
      });
    }

    // Keep presenter view on primary screen
    this.renderPresenterView(document.body);

    // Setup communication between windows
    this.setupWindowCommunication();
  }

  private getWindowFeatures(screen: ScreenInfo, fullSize: boolean): string {
    if (fullSize) {
      return `left=${screen.left},top=${screen.top},width=${screen.width},height=${screen.height}`;
    }
    return `left=${screen.availLeft},top=${screen.availTop},width=${screen.availWidth},height=${screen.availHeight}`;
  }

  private createSlideshowDataUrl(): string {
    const html = `
      <!DOCTYPE html>
      <html>
      <head>
        <style>
          body { margin: 0; background: #000; overflow: hidden; }
          .slide { width: 100vw; height: 100vh; display: flex;
                   align-items: center; justify-content: center;
                   color: white; font-size: 48px; font-family: system-ui; }
        </style>
      </head>
      <body>
        <div class="slide" id="slideContent"></div>
        <script>
          window.addEventListener('message', (e) => {
            if (e.data.type === 'SLIDE_CHANGE') {
              document.getElementById('slideContent').innerHTML = e.data.content;
            }
          });
        </script>
      </body>
      </html>
    `;
    return `data:text/html,${encodeURIComponent(html)}`;
  }

  private renderPresenterView(container: HTMLElement): void {
    container.innerHTML = `
      <div class="presenter-view">
        <div class="current-slide"></div>
        <div class="next-slide"></div>
        <div class="notes"></div>
        <div class="controls">
          <button id="prevSlide">Previous</button>
          <span id="slideCounter">1 / ${this.slides.length}</span>
          <button id="nextSlide">Next</button>
        </div>
        <div class="timer" id="timer">00:00:00</div>
      </div>
    `;

    this.setupPresenterControls(container);
    this.updatePresenterView();
    this.startTimer(container.querySelector('#timer')!);
  }

  private setupPresenterControls(container: HTMLElement): void {
    container.querySelector('#prevSlide')?.addEventListener('click', () => {
      this.previousSlide();
    });

    container.querySelector('#nextSlide')?.addEventListener('click', () => {
      this.nextSlide();
    });

    // Keyboard controls
    document.addEventListener('keydown', (e) => {
      switch (e.key) {
        case 'ArrowRight':
        case 'PageDown':
        case ' ':
          this.nextSlide();
          break;
        case 'ArrowLeft':
        case 'PageUp':
          this.previousSlide();
          break;
        case 'Escape':
          this.endPresentation();
          break;
      }
    });
  }

  private setupWindowCommunication(): void {
    // Send initial slide
    this.sendSlideToWindow();
  }

  private sendSlideToWindow(): void {
    if (this.slideshowWindow) {
      this.slideshowWindow.postMessage({
        type: 'SLIDE_CHANGE',
        content: this.slides[this.currentSlideIndex].content
      }, '*');
    }
  }

  private updatePresenterView(): void {
    const currentSlide = this.slides[this.currentSlideIndex];
    const nextSlide = this.slides[this.currentSlideIndex + 1];

    const currentEl = document.querySelector('.current-slide');
    const nextEl = document.querySelector('.next-slide');
    const notesEl = document.querySelector('.notes');
    const counterEl = document.querySelector('#slideCounter');

    if (currentEl) currentEl.innerHTML = currentSlide.content;
    if (nextEl) nextEl.innerHTML = nextSlide?.content || 'End of presentation';
    if (notesEl) notesEl.textContent = currentSlide.notes || '';
    if (counterEl) counterEl.textContent = `${this.currentSlideIndex + 1} / ${this.slides.length}`;
  }

  nextSlide(): void {
    if (this.currentSlideIndex < this.slides.length - 1) {
      this.currentSlideIndex++;
      this.sendSlideToWindow();
      this.updatePresenterView();
    }
  }

  previousSlide(): void {
    if (this.currentSlideIndex > 0) {
      this.currentSlideIndex--;
      this.sendSlideToWindow();
      this.updatePresenterView();
    }
  }

  goToSlide(index: number): void {
    if (index >= 0 && index < this.slides.length) {
      this.currentSlideIndex = index;
      this.sendSlideToWindow();
      this.updatePresenterView();
    }
  }

  private startTimer(element: HTMLElement): void {
    const startTime = Date.now();

    setInterval(() => {
      const elapsed = Date.now() - startTime;
      const hours = Math.floor(elapsed / 3600000);
      const minutes = Math.floor((elapsed % 3600000) / 60000);
      const seconds = Math.floor((elapsed % 60000) / 1000);

      element.textContent = [hours, minutes, seconds]
        .map(n => n.toString().padStart(2, '0'))
        .join(':');
    }, 1000);
  }

  private addNotesOverlay(): void {
    // For single-monitor mode
    const overlay = document.createElement('div');
    overlay.className = 'notes-overlay';
    overlay.innerHTML = `
      <div class="notes-content">${this.slides[this.currentSlideIndex].notes || ''}</div>
      <button class="toggle-notes">Toggle Notes</button>
    `;
    document.body.appendChild(overlay);
  }

  private renderSlideshow(container: HTMLElement): void {
    container.innerHTML = `
      <div class="slideshow">
        <div class="slide-content">${this.slides[this.currentSlideIndex].content}</div>
      </div>
    `;
  }

  async endPresentation(): Promise<void> {
    // Exit fullscreen
    if (document.fullscreenElement) {
      await document.exitFullscreen();
    }

    // Close slideshow window
    if (this.slideshowWindow) {
      this.slideshowWindow.close();
      this.slideshowWindow = null;
    }
  }
}

2. Multi-Window Trading Dashboard

// trading-dashboard.ts

interface DashboardWindow {
  id: string;
  type: 'chart' | 'watchlist' | 'orderbook' | 'positions' | 'news';
  screen: ScreenInfo;
  bounds: { left: number; top: number; width: number; height: number };
  window?: Window;
}

interface DashboardLayout {
  name: string;
  windows: DashboardWindow[];
}

class TradingDashboard {
  private screenManager = new WindowManagementManager();
  private windows = new Map<string, DashboardWindow>();
  private savedLayouts = new Map<string, DashboardLayout>();

  async initialize(): Promise<void> {
    await this.screenManager.requestPermission();
    this.loadSavedLayouts();

    // Listen for screen changes
    this.screenManager.onScreensChange((screens) => {
      this.handleScreenChange(screens);
    });
  }

  // Save current window arrangement
  async saveLayout(name: string): Promise<void> {
    const layout: DashboardLayout = {
      name,
      windows: Array.from(this.windows.values()).map(w => ({
        id: w.id,
        type: w.type,
        screen: w.screen,
        bounds: this.getWindowBounds(w.window!)
      }))
    };

    this.savedLayouts.set(name, layout);
    localStorage.setItem(
      'trading-layouts',
      JSON.stringify(Array.from(this.savedLayouts.entries()))
    );
  }

  // Restore a saved layout
  async restoreLayout(name: string): Promise<void> {
    const layout = this.savedLayouts.get(name);
    if (!layout) {
      throw new Error(`Layout "${name}" not found`);
    }

    const currentScreens = await this.screenManager.getScreenDetails();

    for (const windowConfig of layout.windows) {
      // Find matching screen or closest alternative
      const targetScreen = this.findMatchingScreen(
        windowConfig.screen,
        currentScreens.screens
      );

      await this.openWindow(
        windowConfig.type,
        targetScreen,
        windowConfig.bounds
      );
    }
  }

  private findMatchingScreen(
    savedScreen: ScreenInfo,
    currentScreens: ScreenInfo[]
  ): ScreenInfo {
    // Try to match by label first
    const byLabel = currentScreens.find(s => s.label === savedScreen.label);
    if (byLabel) return byLabel;

    // Try to match by position
    const byPosition = currentScreens.find(
      s => s.left === savedScreen.left && s.top === savedScreen.top
    );
    if (byPosition) return byPosition;

    // Match by characteristics (internal/external, primary)
    const byType = currentScreens.find(
      s => s.isInternal === savedScreen.isInternal &&
           s.isPrimary === savedScreen.isPrimary
    );
    if (byType) return byType;

    // Fall back to first available
    return currentScreens[0];
  }

  async openWindow(
    type: DashboardWindow['type'],
    screen: ScreenInfo,
    bounds?: DashboardWindow['bounds']
  ): Promise<Window | null> {
    const id = `${type}-${Date.now()}`;
    const url = this.getUrlForType(type);

    const defaultBounds = this.getDefaultBounds(type, screen);
    const finalBounds = bounds || defaultBounds;

    const features = [
      `left=${screen.left + finalBounds.left}`,
      `top=${screen.top + finalBounds.top}`,
      `width=${finalBounds.width}`,
      `height=${finalBounds.height}`
    ].join(',');

    const newWindow = window.open(url, id, features);

    if (newWindow) {
      this.windows.set(id, {
        id,
        type,
        screen,
        bounds: finalBounds,
        window: newWindow
      });

      // Track window close
      newWindow.addEventListener('beforeunload', () => {
        this.windows.delete(id);
      });
    }

    return newWindow;
  }

  private getUrlForType(type: DashboardWindow['type']): string {
    const baseUrl = window.location.origin;
    return `${baseUrl}/dashboard/${type}`;
  }

  private getDefaultBounds(
    type: DashboardWindow['type'],
    screen: ScreenInfo
  ): DashboardWindow['bounds'] {
    const availWidth = screen.availWidth;
    const availHeight = screen.availHeight;

    switch (type) {
      case 'chart':
        return { left: 0, top: 0, width: availWidth * 0.6, height: availHeight };
      case 'watchlist':
        return { left: availWidth * 0.6, top: 0, width: availWidth * 0.4, height: availHeight * 0.5 };
      case 'orderbook':
        return { left: availWidth * 0.6, top: availHeight * 0.5, width: availWidth * 0.4, height: availHeight * 0.5 };
      case 'positions':
        return { left: 0, top: 0, width: availWidth * 0.5, height: availHeight * 0.3 };
      case 'news':
        return { left: availWidth * 0.5, top: 0, width: availWidth * 0.5, height: availHeight * 0.3 };
      default:
        return { left: 0, top: 0, width: 800, height: 600 };
    }
  }

  private getWindowBounds(win: Window): DashboardWindow['bounds'] {
    return {
      left: win.screenLeft || win.screenX || 0,
      top: win.screenTop || win.screenY || 0,
      width: win.outerWidth,
      height: win.outerHeight
    };
  }

  private handleScreenChange(screens: ScreenInfo[]): void {
    // Check if any windows are now on disconnected screens
    for (const [id, dashWindow] of this.windows) {
      const screenStillExists = screens.some(
        s => s.label === dashWindow.screen.label
      );

      if (!screenStillExists && dashWindow.window) {
        // Move window to primary screen
        const primaryScreen = screens.find(s => s.isPrimary) || screens[0];
        const newBounds = this.getDefaultBounds(dashWindow.type, primaryScreen);

        dashWindow.window.moveTo(
          primaryScreen.left + newBounds.left,
          primaryScreen.top + newBounds.top
        );
        dashWindow.window.resizeTo(newBounds.width, newBounds.height);
        dashWindow.screen = primaryScreen;
      }
    }
  }

  private loadSavedLayouts(): void {
    try {
      const saved = localStorage.getItem('trading-layouts');
      if (saved) {
        this.savedLayouts = new Map(JSON.parse(saved));
      }
    } catch (e) {
      console.warn('Failed to load saved layouts');
    }
  }

  // Close all windows
  closeAll(): void {
    for (const [id, dashWindow] of this.windows) {
      dashWindow.window?.close();
    }
    this.windows.clear();
  }
}

Permission & Security

┌─────────────────────────────────────────────────────────────────────┐
│              Window Management Security Model                       │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  PERMISSION REQUIRED                                                │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │                                                              │   │
│  │  The "window-management" permission must be granted to:      │   │
│  │  • Enumerate screens beyond the current one                 │   │
│  │  • Get detailed screen information (labels, isInternal)     │   │
│  │  • Request fullscreen on non-current screens                │   │
│  │                                                              │   │
│  │  Without permission:                                         │   │
│  │  • getScreenDetails() rejects with NotAllowedError          │   │
│  │  • Fallback to basic window.screen info still works         │   │
│  │  • window.open() still works but limited to current screen  │   │
│  │                                                              │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
│  PRIVACY CONSIDERATIONS                                             │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │                                                              │   │
│  │  Screen info can reveal:                                     │   │
│  │  • Number of monitors (somewhat identifying)                │   │
│  │  • Screen resolutions and arrangement                       │   │
│  │  • Display labels (might include manufacturer)              │   │
│  │  • Internal vs external (laptop vs desktop inference)       │   │
│  │                                                              │   │
│  │  Mitigations:                                                │   │
│  │  • Requires explicit user permission                        │   │
│  │  • User can revoke permission in settings                   │   │
│  │  • Permission is per-origin                                 │   │
│  │  • Secure context (HTTPS) required                          │   │
│  │                                                              │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
│  PERMISSIONS POLICY                                                 │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │                                                              │   │
│  │  <!-- Allow window management for this origin -->           │   │
│  │  Permissions-Policy: window-management=(self)                │   │
│  │                                                              │   │
│  │  <!-- Allow in iframe -->                                   │   │
│  │  <iframe allow="window-management" src="..."></iframe>      │   │
│  │                                                              │   │
│  │  <!-- Disable completely -->                                │   │
│  │  Permissions-Policy: window-management=()                    │   │
│  │                                                              │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

Browser Support

FeatureChromeFirefoxSafariEdge
getScreenDetails()100+NoNo100+
Screen change event100+NoNo100+
Fullscreen on specific screen100+NoNo100+
isInternal/isPrimary100+NoNo100+

Note: This is a Chromium-only feature as of 2024.

Key Takeaways

  1. Permission Required: Unlike basic window.screen, full multi-monitor awareness requires user permission.

  2. Graceful Degradation: Always provide fallback behavior using window.screen when permission is denied or API unavailable.

  3. Screen Coordinates: Screens form a virtual coordinate space - left/top can be negative for screens positioned left/above primary.

  4. Layout Persistence: Save window layouts with screen labels for restoration, but handle the case where screens change.

  5. Screen Change Events: Subscribe to screenschange to handle monitors being connected/disconnected.

  6. Fullscreen Control: With permission, you can request fullscreen on any connected screen, not just the current one.

  7. Privacy Aware: The API requires explicit permission because screen configuration can be somewhat identifying.

  8. Chromium-Only: Firefox and Safari don't support this API. Plan for single-monitor fallback on those browsers.

What did you think?

© 2026 Vidhya Sagar Thakur. All rights reserved.