Module 02: Technographics

In Module 01 we sent a minimal beacon with just a URL, title, and timestamp. That tells you what pages are visited, but nothing about who is visiting or how they experience your site. In this module, we collect technographic data — the technical profile of each visitor's browser, device, screen, network, and preferences — and attach it to every beacon.

Demo Files

Run: Open test.html in your browser.

What Are Technographics?

Technographic data describes the user's technical environment: what browser they run, what operating system, how large their screen is, what network conditions they face, and what hardware capabilities they have. This is the data that helps developers understand their actual audience — not just what they assume.

For example: if you discover that 15% of your users are on slow-2g or 2g connections, that changes how you build. You might defer heavy images, reduce JavaScript bundles, or enable a data-saver mode. Without technographic data, you are guessing.

The browser exposes this information through several APIs on the navigator, window, and screen objects. We will collect it all into a single getTechnographics() function.

Collecting Browser & Device Data

User-Agent and Language

The most basic identification: what browser is this, and what language does the user prefer?

const ua = navigator.userAgent;
const language = navigator.language;

The User-Agent string contains browser name, version, operating system, and device information — all packed into a single (often messy) string. For example:

Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36
(KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
Note: User-Agent is being phased out in favor of User-Agent Client Hints (navigator.userAgentData), but the legacy navigator.userAgent string remains widely available and is still the most practical approach for analytics collection today.

Viewport and Screen

Two different measurements matter here: the viewport (what the user sees right now) and the screen (the physical display).

const viewport = {
  width: window.innerWidth,
  height: window.innerHeight
};

const screen = {
  width: window.screen.width,
  height: window.screen.height,
  pixelRatio: window.devicePixelRatio
};

The viewport changes when the user resizes their browser window. The screen dimensions are fixed for the device. devicePixelRatio tells us if they are on a Retina/HiDPI display — a value of 2 means retina (each CSS pixel maps to 4 physical pixels), while 1 means a standard-density display. Values of 3 are common on modern phones.

Hardware

Two APIs reveal the device's computing power:

const hardware = {
  cores: navigator.hardwareConcurrency || 0,
  memory: navigator.deviceMemory || 0
};
deviceMemory returns approximate RAM in GB, quantized to specific values: 0.25, 0.5, 1, 2, 4, 8. Not all browsers support it — Safari and Firefox do not. Always feature-detect with a fallback to 0.

Network Information API

The Network Information API provides estimates of the user's connection quality:

function getNetworkInfo() {
  if (!('connection' in navigator)) return {};

  const conn = navigator.connection;
  return {
    effectiveType: conn.effectiveType,  // 'slow-2g', '2g', '3g', '4g'
    downlink: conn.downlink,            // Mbps estimate
    rtt: conn.rtt,                      // Round-trip time in ms
    saveData: conn.saveData             // User enabled data saver
  };
}

The effectiveType is particularly useful — it classifies the connection into one of four categories based on observed performance, not the underlying technology. A user on Wi-Fi with poor signal might report 2g. The saveData flag indicates the user has explicitly opted into reduced data usage.

Warning: The Network Information API is not available in all browsers (notably Safari). Always feature-detect with if ('connection' in navigator) before accessing. Your collector must gracefully handle its absence by returning an empty object.

Preferences and Context

Modern browsers expose user preferences that can inform how you serve content:

const preferences = {
  colorScheme: window.matchMedia('(prefers-color-scheme: dark)').matches
    ? 'dark' : 'light',
  timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
  cookiesEnabled: navigator.cookieEnabled
};

The color scheme preference tells you whether the user has enabled dark mode at the OS level. The timezone reveals their geographic region (e.g., America/Los_Angeles). And cookieEnabled tells you whether your collector can rely on cookie-based session tracking — or needs an alternative.

Session Identity Without Cookies

Analytics needs to group page views into sessions — a sequence of pages viewed by the same user in a single sitting. The simplest approach: generate a random ID and store it in sessionStorage.

function getSessionId() {
  let sid = sessionStorage.getItem('_collector_sid');
  if (!sid) {
    sid = Math.random().toString(36).substring(2) + Date.now().toString(36);
    sessionStorage.setItem('_collector_sid', sid);
  }
  return sid;
}

This ID persists across page navigations within the same tab, but clears automatically when the tab (or browser) closes. No cookies, no cross-site tracking, no consent banner needed. The tradeoff: opening a new tab starts a new session, and you cannot track users across visits.

The ID itself combines Math.random() (for uniqueness) with a base-36 timestamp (for rough ordering). It is not cryptographically secure, but it does not need to be — it is a session label, not a secret.

Fingerprinting Awareness

Warning: All the data collected above can also be used for browser fingerprinting — uniquely identifying users without cookies. The same APIs serve two very different purposes: legitimate analytics (understanding your audience's capabilities) and invasive tracking (identifying individuals across sites).

The difference is intent and aggregation: analytics aggregates data to understand populations ("40% of our users are on mobile"), while fingerprinting correlates data to track individuals ("this specific combination of screen size, timezone, fonts, and GPU identifies user #4827").

In this tutorial, we collect technographic data for legitimate analytics. Be aware that the same techniques can be misused. See analytics-overview.html Section 7 (Fingerprinting) for more on this distinction.

The Complete getTechnographics() Function

Now we combine everything into a single function that returns the full technographic profile:

function getTechnographics() {
  // Network info (feature-detected)
  let networkInfo = {};
  if ('connection' in navigator) {
    const conn = navigator.connection;
    networkInfo = {
      effectiveType: conn.effectiveType,
      downlink: conn.downlink,
      rtt: conn.rtt,
      saveData: conn.saveData
    };
  }

  return {
    // Browser identification
    userAgent: navigator.userAgent,
    language: navigator.language,
    cookiesEnabled: navigator.cookieEnabled,

    // Viewport (current browser window)
    viewportWidth: window.innerWidth,
    viewportHeight: window.innerHeight,

    // Screen (physical display)
    screenWidth: window.screen.width,
    screenHeight: window.screen.height,
    pixelRatio: window.devicePixelRatio,

    // Hardware
    cores: navigator.hardwareConcurrency || 0,
    memory: navigator.deviceMemory || 0,

    // Network
    network: networkInfo,

    // Preferences
    colorScheme: window.matchMedia('(prefers-color-scheme: dark)').matches
      ? 'dark' : 'light',
    timezone: Intl.DateTimeFormat().resolvedOptions().timeZone
  };
}

Every property uses feature detection or provides a safe fallback. The function returns a plain object — no side effects, no async, no dependencies.

Integrating with the Collector

Our collector-v2.js extends the Module 01 collector by adding technographics and a session ID to every payload:

function collect() {
  const payload = {
    url: window.location.href,
    title: document.title,
    referrer: document.referrer,
    timestamp: new Date().toISOString(),
    type: 'pageview',
    session: getSessionId(),
    technographics: getTechnographics()
  };

  const blob = new Blob([JSON.stringify(payload)], { type: 'application/json' });
  const endpoint = 'https://example.com/collect';

  if (navigator.sendBeacon) {
    navigator.sendBeacon(endpoint, blob);
  }

  console.log('[collector-v2] payload:', payload);
}

Compare this to the Module 01 payload — we have added two new fields: session (a stable ID for grouping page views) and technographics (the full technical profile). The beacon delivery mechanism is identical.

Cross-Reference: The Reference Collector

Reference Implementation: The reference collector.js file uses three separate functions for what we combined into getTechnographics():

The reference implementation also computes derived values like isLowEndDevice and isLowEndExperience based on hardware and network thresholds. We will revisit device classification in later modules.

See analytics-overview.html Section 4 (What Can Be Collected) and Section 7 (Fingerprinting) for the conceptual foundations.

Summary