Chat history logs are highly sensitive documents containing names, phone numbers, and private conversations. Standard analytics apps resolve parsing by sending raw log text files to a cloud server. For the WhatsApp Dashboard, I took a privacy-first approach: engineering a parser that runs completely client-side in the user's browser, ensuring no data ever gets uploaded.

In this log, I'll show how to parse varying multiline OS text logs with optimized JavaScript regular expressions and render custom, responsive visual charts with zero external charting dependencies.

1. The Regex Parsing Pipeline

WhatsApp exports logs differently depending on the operating system. iOS exports logs with bracketed dates (e.g., [12/07/26, 09:30:15] Name: Message), while Android uses dash separators (e.g., 12/07/2026, 09:30 - Name: Message).

Additionally, users frequently send multiline messages (e.g., messages with carriage returns). The parser must recognize when a line is a continuation of the previous message rather than the start of a new one.

// Parsing rules for iOS and Android
const IOS_PATTERN = /^\[(\d{2}\/\d{2}\/\d{2,4}),\s(\d{2}:\d{2}(?::\d{2})?)\]\s([^:]+):\s([\s\S]*)$/;
const ANDROID_PATTERN = /^(\d{2}\/\d{2}\/\d{2,4}),\s(\d{2}:\d{2})\s-\s([^:]+):\s([\s\S]*)$/;

function parseChatLog(rawText) {
    const lines = rawText.split('\n');
    const messages = [];
    let currentMessage = null;
    
    for (let i = 0; i < lines.length; i++) {
        const line = lines[i];
        
        // Check if line matches a new message header
        const iosMatch = line.match(IOS_PATTERN);
        const androidMatch = line.match(ANDROID_PATTERN);
        const match = iosMatch || androidMatch;
        
        if (match) {
            // Push previous message if exists
            if (currentMessage) messages.push(currentMessage);
            
            currentMessage = {
                date: match[1],
                time: match[2],
                sender: match[3].trim(),
                body: match[4]
            };
        } else if (currentMessage) {
            // Line is a continuation of the previous multiline message
            currentMessage.body += '\n' + line;
        }
    }
    
    if (currentMessage) messages.push(currentMessage);
    return messages;
}

2. Aggregating Analytics Arrays

Once the log is parsed into an array of message structures, the analytics engine builds frequency maps for hourly activity, active weekdays, and emoji occurrences:

function analyzeHourlyActivity(messages) {
    const hourMap = Array(24).fill(0);
    messages.forEach(msg => {
        // Extract hour digit (e.g. "09" -> 9)
        const hour = parseInt(msg.time.split(':')[0], 10);
        if (!isNaN(hour) && hour >= 0 && hour < 24) {
            hourMap[hour]++;
        }
    });
    return hourMap;
}

3. Zero-Dependency Charting using Custom SVGs

Loading heavy canvas/chart dependencies (e.g. Chart.js) increases loading latency and security maintenance overhead. To keep the app under 20KB, I designed a reactive SVG generator that renders responsive chart bars dynamically.

Here is the implementation for generating a responsive SVG time-series chart from an array of hourly stats:

function renderSvgChart(hourData) {
    const maxVal = Math.max(...hourData) || 1;
    const chartHeight = 120;
    const barWidth = 18;
    const barGap = 6;
    const svgWidth = (barWidth + barGap) * 24;
    
    let barsHtml = '';
    hourData.forEach((val, hour) => {
        const barHeight = (val / maxVal) * chartHeight;
        const x = hour * (barWidth + barGap);
        const y = chartHeight - barHeight;
        
        barsHtml += `
            <rect 
                x="${x}" 
                y="${y}" 
                width="${barWidth}" 
                height="${barHeight}" 
                fill="var(--accent-color)" 
                stroke="var(--border-color)" 
                stroke-width="1.5"
                rx="2"
            >
                <title>${hour}:00 - ${val} messages</title>
            </rect>
        `;
    });
    
    return `
        <svg viewBox="0 0 ${svgWidth} ${chartHeight}" width="100%" height="auto">
            ${barsHtml}
        </svg>
    `;
}

4. Hour Heatmaps with CSS Grid

To visualize weekly messaging habits, I used a 7x24 CSS Grid block. Each grid box's background opacity is scaled relative to its messaging frequency, establishing a light and performant visual heatmap without canvas rendering overhead.

.heatmap-container {
  display: grid;
  grid-template-columns: repeat(24, 1fr);
  grid-template-rows: repeat(7, 1fr);
  gap: 2px;
  background: var(--border-color);
  border: 2px solid var(--border-color);
  border-radius: 4px;
}

.heatmap-cell {
  aspect-ratio: 1;
  background: var(--accent-color);
  /* Opacity is set dynamically by JS based on usage weight */
}

Conclusion

This dashboard demonstrates that web tools don't need heavy frameworks or server backends to be powerful. By utilizing native Javascript Regex for text parsing, simple arithmetic maps, and standard SVGs/CSS grids for layout representation, we preserve user privacy and deliver high-performance visual tools.