- The Problem: Is It Actually Hot or Just Noise?
- Four Meta Keys — Everything You Need
- Part I: The Linear Approach — Foundation
- Linear Projection
- The Early-Morning Problem: Linear Noise
- The Volume Problem: Small Titles Get Shortchanged
- Combining Day and Week — Momentum Signal
- Confidence Weighting Over Time
- Part II: Integrating Traffic Shape Learner
- Traffic Is Not Evenly Distributed — And That’s a Big Problem
- How Traffic Shape Learner Works
- From Clock Time to Traffic CDF
- Impact Across the Entire System
- End-of-Day View Forecasting — Hybrid Bayesian Smoothing
- Additional Signals from 4 Meta Keys
- Performance: One Query, Thousands of Titles
- Conclusion
The Problem: Is It Actually Hot or Just Noise?
Suppose at 9 AM a manga title has 80 views. The natural question is: is this number noteworthy? The answer depends entirely on context — how many views did the same title have at this hour yesterday? What about last week? And what percentage of daily traffic does 9 AM typically represent?
If we only look at absolute numbers, we will always bias toward titles that were already large. HeatWave solves this by measuring relative growth velocity, based on each title’s own history — without cross-comparing between different titles.
Four Meta Keys — Everything You Need
The entire system relies on only 4 values stored on each post:
_init_view_day_count— today’s views (from 00:00 to now)_init_view_day_yesterday— yesterday’s total views (full day)_init_view_week_count— this week’s views (from start of week to now)_init_view_week_last— last week’s total views (full week)
These are all standard values in any view-counting system. No new schema, no log tables, no heavy processing queues.
Part I: The Linear Approach — Foundation
Linear Projection
The first and simplest idea: if a title has reached X views after p percent of the day has elapsed, then by end of day it is projected to reach X / p views.
// Linear elapsed ratio: current hour / 24
$elapsed = ($hour + 1) / 24;
// Project end-of-day views
$projected = $current_views / $elapsed;
// Growth rate vs yesterday
$raw_growth = ($projected - $yesterday) / $yesterday;
If $raw_growth is positive and large enough, the title is “hot.” This is the foundation of the entire algorithm — and also the first weakness that needs addressing.
The Early-Morning Problem: Linear Noise
At 2 AM, the elapsed ratio is only 0.125 (3/24). If a title just got 10 views from a random referral, linear projection predicts 10 / 0.125 = 80 views — and if yesterday it only had 30, the growth rate looks impressive even though it’s just noise.
To fix this, we set a minimum elapsed threshold — for example 0.33 (around 8 AM) — before any calculation runs. And we add a minimum volume filter: if the current accumulated views are too small relative to yesterday, we consider the data insufficiently reliable.
function init_html_calc_growth_score_linear(
int $current,
int $previous,
float $elapsed
): ?float {
// Not enough time has elapsed
if ($elapsed < 0.33) return null;
// Yesterday lacks baseline
if ($previous < 50) return null;
// Current views too small to trust
if ($current < $previous * 0.05) return null;
$projected = $current / $elapsed;
$raw_growth = ($projected - $previous) / $previous;
return $raw_growth;
}
The Volume Problem: Small Titles Get Shortchanged
In a purely linear model, a title growing from 20 to 40 views (+100%) ranks equally with one growing from 2,000 to 4,000 views. Signals from small titles are too noisy — a few clicks from a single share can create a massive phantom growth rate.
The simplest linear fix is a volume-based damping factor: small-title scores are multiplied by a coefficient less than 1. For example, if the site’s “normal” threshold is 400 views/day, a title that only had 100 views/day yesterday gets its score reduced to sqrt(100/400) = 0.5.
$site_target = 400; // Adjust to your site's scale
if ($previous < $site_target) {
$low_volume_factor = sqrt($previous / $site_target);
$score *= $low_volume_factor;
}
Combining Day and Week — Momentum Signal
Looking at only one dimension (day or week) is easily fooled. A title might spike today but the weekly trend is declining (e.g., a new chapter last week, nothing this week). Conversely, a title growing steadily on both day and week shows a more durable signal.
We compute two scores in parallel — one for day, one for week — then take the better score. If both are positive, we apply a momentum bonus. If this week is declining sharply compared to last week (with sufficient data), we penalize the score.
$best = max(array_filter([$day_score, $week_score], fn($s) => $s !== null));
// Both dimensions positive: momentum bonus
if ($day_score > 0 && $week_score > 0) {
$best *= 1.15;
}
// This week declining significantly vs last week: penalty
if ($week_last >= 50 && $week < $week_last * 0.65 && $week_elapsed >= 0.5) {
$best *= 0.65;
}
Confidence Weighting Over Time
In the advanced linear stage, we add a time-based confidence layer: the score is not just raw growth rate, but also multiplied by sqrt(elapsed). The result: at the same growth rate, data recorded in the evening carries more weight than data from early morning.
$confidence = sqrt($elapsed);
$score = $raw_growth * $confidence;
This completes the linear-phase model. It works well under normal conditions, but still carries an unaddressed implicit assumption: that traffic distributes evenly throughout the day and week — which is almost never true.
Part II: Integrating Traffic Shape Learner
Traffic Is Not Evenly Distributed — And That’s a Big Problem
On most content sites, traffic does not split evenly across 24 hours. The common reality: very few people online at 3-4 AM, traffic starts rising from 7-8 AM, peaks in the evening, then gradually declines. If we use linear elapsed (hour / 24) to project numbers, we are assuming 10% of traffic has passed by 2:24 AM — when in reality it might be less than 1%.
The consequence: a title with 50 views at 8 AM (when traffic is just beginning) gets under-projected compared to reality, and we might miss a genuinely hot signal.
Traffic Shape Learner solves this precisely. This module learns the site’s traffic distribution by hour of day and day of week, using Exponential Moving Average (EMA) combined with Bayesian Prior for data smoothing.
How Traffic Shape Learner Works
Each time a view is recorded, the module updates the “bin” for the current hour:
// Record view by site hour
$hour = (int) wp_date('G', $now_gmt);
$state['hour_bins'][$hour] += 1;
$state['total'] += 1;
At the end of each day (at reset time), the system runs rollup: it calculates the percentage of traffic for each hour relative to yesterday’s total traffic, then updates the EMA to keep learning:
// Calculate hourly share, with Bayesian Prior to fight noise
$alpha = 0.25; // Learning rate
$kappa = 48; // Prior strength (equivalent to 48 "default" views per hour)
for ($h = 0; $h < 24; $h++) {
$num = $hour_bins[$h] + $kappa * (1.0 / 24.0);
$share[$h] = $num / ($total + $kappa);
$mult = $share[$h] / (1.0 / 24.0); // Compare to uniform distribution
$shape[$h] = (1 - $alpha) * $shape[$h] + $alpha * $mult;
}
// Normalize to mean = 1
After a few weeks of learning, the shape['hour'] array contains 24 elements, each a multiplier relative to uniform distribution. For example, hour 21 might have shape = 1.8 (80% above average), while hour 3 has shape = 0.15 (only 15% of average).
From Clock Time to Traffic CDF
With the learned shape, the elapsed ratio calculation is no longer a simple division:
function init_html_get_day_elapsed_ratio(): float {
$hour = (int) current_time('G');
$shapes = /* from Traffic Shape Learner */;
if (is_array($shapes) && isset($shapes['hour'])) {
// Cumulative: total traffic from 00:00 to current hour
$elapsed_sum = 0;
for ($i = 0; $i <= $hour; $i++) { $elapsed_sum += (float) ($shapes['hour'][$i] ?? 1.0); } $total_sum = array_sum($shapes['hour']); return $total_sum > 0 ? ($elapsed_sum / $total_sum) : (($hour + 1) / 24);
}
// Fallback to linear if no data yet
return ($hour + 1) / 24;
}
This is the Cumulative Distribution Function (CDF) of actual traffic. At 8 AM, instead of always returning 0.375 (9/24) like the linear model, this function might return 0.12 — reflecting the reality that only 12% of the day’s traffic has arrived by that time.
Impact Across the Entire System
When the elapsed ratio is accurate, all upstream calculations become more reliable:
First, the minimum elapsed threshold can drop from 0.33 to 0.10 — meaning the system can detect hot titles significantly earlier (as soon as 10% of the day’s actual traffic has arrived, instead of waiting until 8 AM).
Second, Volume Guard (the filter blocking titles with too few views) also becomes smarter. Instead of applying a hard threshold regardless of time of day, we adjust the threshold to match actual traffic flow:
// Peak morning hours: require higher minimum to avoid phantom spikes
if ($elapsed_ratio < 0.5 && $current < ($previous * 0.12)) {
return null; // Insufficient signal
}
Third, Breakout Detection — the mechanism for spotting titles that explode unexpectedly — also uses the same elapsed ratio for its projection:
function init_html_is_breakout(int $current, int $previous, float $elapsed): bool {
if ($elapsed < 0.10 || $previous < 50) return false; $projected = $current / $elapsed; return ($projected / $previous) >= 4.5; // Projected to reach 4.5x baseline
}
A title projected to hit 4.5x yesterday’s views — even mid-day — gets marked “breakout” immediately, without waiting for a high elapsed ratio.
End-of-Day View Forecasting — Hybrid Bayesian Smoothing
Alongside hot-title detection, the system can also forecast total end-of-day views. This is useful for real-time leaderboards and content recommendation systems.
The naive approach — projected = current / elapsed — works well late in the day but is highly unstable early on: a small morning spike can inflate the forecast by 10x the actual outcome.
The solution is Hybrid Bayesian Smoothing: combining the actual projection with yesterday’s baseline, with weights depending on elapsed ratio:
function init_html_predict_eod_views(int $post_id, array $meta): int {
$current = $meta['day'];
$yesterday = $meta['day_yesterday'];
if ($current <= 0) return $yesterday;
$elapsed = init_html_get_day_elapsed_ratio();
$pure_projected = $current / $elapsed;
// Early day: trust yesterday's baseline more
// Late day: trust the actual unfolding data
$weight = $elapsed;
$predicted = ($pure_projected * $weight) + ($yesterday * (1 - $weight));
return max($current, (int) round($predicted));
}
When elapsed = 0.10 (early morning), the formula gives 10% weight to the actual projection and 90% to yesterday’s baseline — the forecast won’t jump wildly from a spike. When elapsed = 0.90 (near end of day), the weights reverse and the forecast hugs reality closely. The transition curve is smooth, with no sudden jumps.
Additional Signals from 4 Meta Keys
Beyond hot detection and view forecasting, the same 4 meta keys enable two more valuable metrics:
Weekly Momentum Status — based on week-over-week growth velocity, titles can be classified into groups: “Skyrocketing” (up 50%+), “Gaining Momentum” (up 10-50%), “Holding Steady” (within normal fluctuation), or “Cooling Down” (down 30%+). This metric is more useful than absolute numbers when displaying on author pages or admin dashboards.
Record Breaker Detection — checking whether today’s views have already surpassed yesterday’s full-day total, even before the day ends. This is the strongest signal extractable from 4 meta keys: no projection, no estimation, just direct comparison.
function init_html_is_record_breaker(array $meta): bool {
return $meta['day_yesterday'] >= 50
&& $meta['day'] > $meta['day_yesterday'];
}
Performance: One Query, Thousands of Titles
The entire system was designed from the ground up to run in loops across hundreds or even thousands of titles:
Day and week elapsed ratios — computed once via static cache inside the function, then reused across the entire loop. Traffic shape cached via transient with a 2-hour TTL, no database queries per request. All 4 meta keys loaded into object cache with a single update_post_meta_cache($post_ids) call before entering the loop.
// Prime cache once for the entire list
init_html_prime_view_meta_cache($post_ids);
// Compute elapsed once outside the loop
$day_elapsed = init_html_get_day_elapsed_ratio();
$week_elapsed = init_html_get_week_elapsed_ratio();
$threshold = init_html_get_hot_threshold();
foreach ($posts as $post) {
$meta = init_html_get_view_meta($post->ID);
$score = init_html_calc_hot_score($meta, $day_elapsed, $week_elapsed);
$results[$post->ID] = $score >= $threshold;
}
Conclusion
HeatWave is proof that a real-time trend detection system does not need complex infrastructure. With just 4 meta keys and a background traffic-distribution learning module, we can build a deep analytics engine: early breakout detection, morning-noise filtering, end-of-day view forecasting, weekly momentum tracking, and real-time record breaking — all in real time.
The key design decision is the clean separation between two layers: the pure linear layer (independent of Traffic Shape) that works from day one, and the Traffic Shape Learner layer that gradually upgrades over time as the site accumulates enough real data. These two layers converge at a single point — the elapsed ratio function — keeping the entire system compact and maintainable.
Comments