Loading
Event Processing Time

Event Processing Time

Analyzes the time spent in each phase of page navigation, from initial redirect to the load event. This helps identify bottlenecks in the page loading process and understand where time is being spent.

Key phases in page load:

What each phase means:

PhaseDescriptionImpact
RedirectTime following HTTP redirectsEach redirect adds latency
DNS LookupDomain name resolutionSlow DNS affects all requests
TCP ConnectionEstablishing connectionGeographic distance matters
SSL/TLSSecure handshakeCertificate chain complexity
Request/ResponseServer processing timeBackend performance
DOM ProcessingParsing HTML, building DOMHTML complexity
DOMContentLoadedDOM ready, executing deferred scriptsScript execution time
Load EventAll resources loadedTotal page weight

Snippet

// Event Processing Time - Analyze page load phases
// https://webperf-snippets.nucliweb.net
 
(() => {
  const phases = [
    { name: "Redirect", start: "redirectStart", end: "redirectEnd", icon: "🔄" },
    { name: "DNS Lookup", start: "domainLookupStart", end: "domainLookupEnd", icon: "🔍" },
    { name: "TCP Connection", start: "connectStart", end: "connectEnd", icon: "🔌" },
    { name: "SSL/TLS", start: "secureConnectionStart", end: "connectEnd", icon: "🔒" },
    { name: "Request", start: "requestStart", end: "responseStart", icon: "📤" },
    { name: "Response", start: "responseStart", end: "responseEnd", icon: "📥" },
    { name: "DOM Processing", start: "responseEnd", end: "domContentLoadedEventStart", icon: "🏗️" },
    { name: "DOMContentLoaded", start: "domContentLoadedEventStart", end: "domContentLoadedEventEnd", icon: "📄" },
    { name: "Resource Loading", start: "domContentLoadedEventEnd", end: "loadEventStart", icon: "📦" },
    { name: "Load Event", start: "loadEventStart", end: "loadEventEnd", icon: "✅" },
  ];
 
  new PerformanceObserver((list) => {
    const [entry] = list.getEntries();
 
    const formatMs = (ms) => ms.toFixed(2) + " ms";
    const formatBar = (ms, total) => {
      const pct = total > 0 ? (ms / total) * 100 : 0;
      const width = Math.min(Math.round(pct / 2.5), 40);
      return "█".repeat(width) + "░".repeat(Math.max(0, 20 - width));
    };
 
    // Calculate all phase durations
    const phaseData = phases
      .map((phase) => {
        const start = entry[phase.start] || 0;
        const end = entry[phase.end] || 0;
        let duration = end - start;
 
        // Handle SSL which shares end with TCP
        if (phase.name === "SSL/TLS" && entry.secureConnectionStart > 0) {
          duration = entry.connectEnd - entry.secureConnectionStart;
        } else if (phase.name === "TCP Connection" && entry.secureConnectionStart > 0) {
          duration = entry.secureConnectionStart - entry.connectStart;
        }
 
        return {
          ...phase,
          start,
          end,
          duration: Math.max(0, duration),
        };
      })
      .filter((p) => p.duration > 0 || p.name === "Redirect");
 
    const totalTime = entry.loadEventEnd - entry.startTime;
    const networkTime = (entry.responseEnd || 0) - entry.startTime;
    const processingTime = totalTime - networkTime;
 
    // Rating based on total load time
    let rating, color;
    if (totalTime <= 2500) {
      rating = "Fast";
      color = "#22c55e";
    } else if (totalTime <= 4000) {
      rating = "Moderate";
      color = "#f59e0b";
    } else {
      rating = "Slow";
      color = "#ef4444";
    }
 
    console.group(
      `%c⏱️ Page Load: ${totalTime.toFixed(0)}ms (${rating})`,
      `color: ${color}; font-weight: bold; font-size: 14px;`
    );
 
    // Summary
    console.log("");
    console.log("%cSummary:", "font-weight: bold;");
    console.log(`   Network time: ${networkTime.toFixed(0)}ms (${((networkTime / totalTime) * 100).toFixed(0)}%)`);
    console.log(`   Processing time: ${processingTime.toFixed(0)}ms (${((processingTime / totalTime) * 100).toFixed(0)}%)`);
    console.log(`   Total: ${totalTime.toFixed(0)}ms`);
 
    // Timeline visualization
    console.log("");
    console.log("%cTimeline:", "font-weight: bold;");
 
    phaseData.forEach(({ name, duration, icon }) => {
      const pct = (duration / totalTime) * 100;
      const bar = formatBar(duration, totalTime);
      const durationStr = formatMs(duration).padStart(12);
      const pctStr = `${pct.toFixed(1)}%`.padStart(7);
 
      // Color code long phases
      let phaseColor = "";
      if (duration > totalTime * 0.3) {
        phaseColor = "color: #ef4444;"; // Red for >30%
      } else if (duration > totalTime * 0.15) {
        phaseColor = "color: #f59e0b;"; // Orange for >15%
      }
 
      console.log(
        `%c${icon} ${name.padEnd(18)} ${durationStr} ${pctStr}  ${bar}`,
        phaseColor
      );
    });
 
    // Key milestones
    console.log("");
    console.log("%cKey Milestones:", "font-weight: bold;");
    console.log(`   TTFB (Time to First Byte): ${entry.responseStart.toFixed(0)}ms`);
    console.log(`   DOMContentLoaded: ${entry.domContentLoadedEventEnd.toFixed(0)}ms`);
    console.log(`   Load Complete: ${entry.loadEventEnd.toFixed(0)}ms`);
 
    // Find bottleneck
    const longestPhase = phaseData.reduce((a, b) =>
      a.duration > b.duration ? a : b
    );
 
    if (longestPhase.duration > totalTime * 0.25) {
      console.log("");
      console.log("%c💡 Bottleneck Detected:", "color: #3b82f6; font-weight: bold;");
      console.log(
        `   ${longestPhase.name} takes ${((longestPhase.duration / totalTime) * 100).toFixed(0)}% of total load time`
      );
 
      const tips = {
        "Redirect": "Minimize redirects. Update links to final URLs.",
        "DNS Lookup": "Use DNS prefetching (<link rel='dns-prefetch'>). Consider faster DNS.",
        "TCP Connection": "Enable HTTP/2 or HTTP/3. Use connection preconnect.",
        "SSL/TLS": "Enable TLS 1.3. Optimize certificate chain. Use session resumption.",
        "Request": "Optimize server response time. Add caching headers.",
        "Response": "Enable compression. Reduce HTML size. Use streaming.",
        "DOM Processing": "Simplify HTML structure. Defer non-critical scripts.",
        "DOMContentLoaded": "Reduce synchronous JavaScript. Use defer/async.",
        "Resource Loading": "Lazy load images. Prioritize critical resources.",
        "Load Event": "Reduce page weight. Optimize resource loading order.",
      };
 
      console.log(`   Tip: ${tips[longestPhase.name] || "Investigate this phase."}`);
    }
 
    console.groupEnd();
  }).observe({ type: "navigation", buffered: true });
})();

Understanding the Results

The snippet displays a timeline of all page load phases with:

Summary Section:

  • Network time: Time spent on network operations (DNS, TCP, SSL, request/response)
  • Processing time: Time spent on DOM parsing and JavaScript execution
  • Total: Complete page load time

Timeline Section:

Each phase shows:

  • Duration in milliseconds
  • Percentage of total load time
  • Visual bar indicating relative duration
  • Color coding: 🔴 Red (>30%), 🟠 Orange (>15%), default otherwise

Key Milestones:

  • TTFB: When the first byte of HTML arrives
  • DOMContentLoaded: When HTML is parsed and deferred scripts executed
  • Load Complete: When all resources (images, styles, etc.) are loaded

Bottleneck Detection:

If any phase takes more than 25% of total load time, the snippet identifies it and provides optimization tips.

Typical Values

MetricGoodModerateSlow
Total Load Time< 2.5s2.5s - 4s> 4s
TTFB< 800ms800ms - 1800ms> 1800ms
DOMContentLoaded< 1.5s1.5s - 3s> 3s

Further Reading