ApexCharts Weekly Activity Heatmap (Day × Hour) — Free Snippet

ApexCharts Weekly Activity Heatmap (Day × Hour) · Charts · Plain HTML, CSS & JS · Live preview

CategoryCharts

What's included

Features

7×24 heatmap grid
Days as series, hours as points.
Monday-first ordering
Series reversed to counter bottom-up stacking.
Fixed colour bands
Named ranges with a stable meaning and a legend.
Correct time-zone shifting
A flat 168-hour week wraps across day boundaries.
Busiest-hour summary
Key insight stated in text.
Uncluttered axis
Every third hour labelled, no rotated text.
Deterministic sample data
Seeded values render identically on each load.
Cell gaps and rounding
White strokes and small radius separate cells.

About this UI Snippet

ApexCharts Day × Hour Heatmap — Finding the Busy Hours in a Week

Screenshot of the ApexCharts Weekly Activity Heatmap (Day × Hour) snippet rendered live

A week of hourly counts is 168 numbers. As a table it is unreadable; as a day-by-hour heatmap the pattern jumps out immediately: the weekday afternoon block, the lunch dip, the quiet weekend. This layout is used for support staffing, posting schedules, server load and store footfall.

Series are rows, points are columns

In an ApexCharts heatmap each series is one row and each { x, y } point is a cell. The snippet builds seven series (days), each with 24 points (hours). ApexCharts stacks series from the bottom up, so the array is reversed to put Monday on top.

Fixed colour ranges instead of shades

By default heatmap cells are shaded relative to the data. That makes colours mean different things after the data changes. Here enableShades: false and colorScale.ranges define four named bands with fixed boundaries. The legend lists them, and "Busy" means 13–20 conversations no matter which time zone you view.

Time zones cross midnight

Converting from UTC to another zone is not a per-row operation. Moving New York five hours back pushes Monday 02:00 UTC into Sunday evening. The snippet flattens the week to 168 hours, shifts every index with wrap-around (((i + shift) % 168 + 168) % 168), and rebuilds the grid. Half-hour zones are rounded to the nearest hour, which is noted as a limitation you may need to handle.

A text summary

The busiest day and hour are computed from the same grid and written under the chart, so the key insight is available without reading colours.

Readable axis labels

Only every third hour is labelled on the x-axis, keeping labels horizontal without collisions.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Hand this snippet to an AI assistant such as Claude and ask it to explain the 168-hour wrap-around and why shifting each day's row independently would be wrong. Useful extensions to ask for: bucketing raw event timestamps into the grid, handling daylight saving changes, a toggle between count and percent-of-week, or clicking a cell to list the underlying events. Ask it to review whether the colour bands suit your real distribution.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a day-by-hour activity heatmap with ApexCharts (loaded from a CDN) in plain HTML, CSS and JavaScript.

Requirements:
- Store a week of hourly counts in UTC as a 7×24 grid with a realistic weekday office-hours pattern, a lunch dip and quieter weekends, using a seeded random generator.
- Render seven series (Monday to Sunday) with 24 hourly points each, with Monday at the top.
- Use four fixed, named colour ranges instead of automatic shading, shown in a bottom legend.
- Label only every third hour on the x-axis and show counts in the tooltip.
- Add a time-zone select (UTC, UTC−5, UTC+1, UTC+5:30) that shifts the data correctly across day boundaries by treating the week as 168 continuous hours with wrap-around.
- Show the busiest day and hour as a text summary under the chart and update it when the zone changes.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Source Code

Requires
<div class="ahm-wrap">
  <div class="ahm-card">
    <div class="ahm-head">
      <div>
        <div class="ahm-title">When do customers message support?</div>
        <div class="ahm-sub">Average conversations started per hour, last 8 weeks</div>
      </div>
      <label class="ahm-tz">Time zone
        <select id="ahmShift">
          <option value="0">UTC</option>
          <option value="-5">New York (UTC−5)</option>
          <option value="1">Berlin (UTC+1)</option>
          <option value="5.5">Mumbai (UTC+5:30)</option>
        </select>
      </label>
    </div>
    <div id="ahmChart"></div>
    <div class="ahm-peak" id="ahmPeak"></div>
  </div>
</div>

Step by step

How to Use

  1. 1
    Load ApexChartsInclude apexcharts.min.js from the CDN.
  2. 2
    Paste the snippetA 7-day by 24-hour grid renders with a four-band legend.
  3. 3
    Hover cellsThe tooltip shows the day, hour and count.
  4. 4
    Change time zoneThe grid shifts, wrapping hours into the previous or next day.
  5. 5
    Read the summaryThe busiest hour is stated in text below the chart.
  6. 6
    Load real dataFill BASE[day][hour] with your own UTC counts.

Real-world uses

Common Use Cases

Support staffing schedules
Schedule agents for the hours that actually bring tickets. The weekday-afternoon block and the lunch dip jump out of 168 numbers immediately.
Social posting schedules
Find when an audience is most engaged and plan posts around the busiest hours, with the busiest-hour summary stating the answer directly.
Infrastructure load planning
Spot recurring weekly traffic peaks for capacity planning. Fixed colour bands keep their meaning stable, so a dark cell always represents the same load.
Retail footfall analysis
See visits by weekday and hour, and switch time zone to compare stores. The 168-hour week wraps correctly across day boundaries.
Personal productivity tracking
Visualise when you commit code or do focused work. Monday sits at the top, with the series reversed to counter ApexCharts' bottom-up stacking.
Related: ECharts Calendar Heatmap
For a year-long daily view see ECharts Calendar Heatmap.
Related: Activity Heatmap
A dependency-free take in Activity Heatmap.

Got questions?

Frequently Asked Questions

An array of series, one per row. Each series has a name and a data array of { x, y } points, where x is the column label and y the value. Seven series with 24 points each gives a day-by-hour grid.

ApexCharts draws heatmap series from the bottom of the chart upward. Reversing the array puts the first day, Monday, at the top.

Set plotOptions.heatmap.enableShades to false and define plotOptions.heatmap.colorScale.ranges as objects with from, to, color and name. The names appear in the legend.

Store data in UTC, flatten the week into 168 hourly values, shift each index by the zone offset with modular wrap-around, and rebuild the 7×24 grid. Shifting row by row is wrong, because hours near midnight belong to the neighbouring day.

With hourly buckets a 30-minute offset can't be represented exactly, so this snippet rounds it. For exact results, bucket raw timestamps by local hour after converting them, instead of shifting pre-aggregated hours.