Introduction
How do you programmatically detect when a video is going viral? This is the core problem I faced when building ViralVidVault — a platform that surfaces trending and viral videos from 7 European regions. In this article, I'll walk through the virality scoring algorithm that powers the platform.
The Concept: Virality as a Composite Score
A video doesn't go viral because of a single metric. It's the combination of velocity (how fast views are growing), engagement (likes, comments relative to views), and share momentum (how quickly sharing is accelerating). Our algorithm combines these into a single score between 0 and 100.
The Data Model
First, we need a structure to hold our metrics:
<?php
class VideoMetrics
{
public function __construct(
public readonly string $videoId,
public readonly int $viewsNow,
public readonly int $viewsPrevious,
public readonly int $likes,
public readonly int $comments,
public readonly int $shares,
public readonly int $timeDeltaSeconds,
public readonly string $region = 'US',
) {}
public function viewVelocity(): float
{
if ($this->timeDeltaSeconds <= 0) return 0;
$delta = $this->viewsNow - $this->viewsPrevious;
return ($delta / $this->timeDeltaSeconds) * 3600; // views per hour
}
public function engagementRate(): float
{
if ($this->viewsNow <= 0) return 0;
return (($this->likes + $this->comments * 2) / $this->viewsNow) * 100;
}
}
The Scoring Engine
Now the core scorer. Each component is normalized to a 0-100 scale using regional baselines:
<?php
class ViralityScorer
{
private const WEIGHTS = [
'velocity' => 0.35,
'engagement' => 0.25,
'shares' => 0.25,
'sentiment' => 0.15,
];
private const REGIONAL_BASELINES = [
'US' => ['velocity' => 50000, 'engagement' => 5.0],
'GB' => ['velocity' => 20000, 'engagement' => 4.5],
'PL' => ['velocity' => 10000, 'engagement' => 6.0],
'NL' => ['velocity' => 8000, 'engagement' => 5.5],
'SE' => ['velocity' => 5000, 'engagement' => 5.0],
'NO' => ['velocity' => 4000, 'engagement' => 5.0],
'AT' => ['velocity' => 6000, 'engagement' => 5.0],
];
public function calculate(VideoMetrics $metrics): ViralityScore
{
$baseline = self::REGIONAL_BASELINES[$metrics->region] ?? self::REGIONAL_BASELINES['US'];
$velocityNorm = min(100, ($metrics->viewVelocity() / $baseline['velocity']) * 100);
$engagementNorm = min(100, ($metrics->engagementRate() / $baseline['engagement']) * 100);
$sharesNorm = min(100, ($metrics->shares / max(1, $metrics->viewsNow)) * 1000);
$sentimentNorm = $this->estimateSentiment($metrics);
$score = ($velocityNorm * self::WEIGHTS['velocity'])
+ ($engagementNorm * self::WEIGHTS['engagement'])
+ ($sharesNorm * self::WEIGHTS['shares'])
+ ($sentimentNorm * self::WEIGHTS['sentiment']);
return new ViralityScore(
value: round($score, 1),
components: compact('velocityNorm', 'engagementNorm', 'sharesNorm', 'sentimentNorm'),
region: $metrics->region,
);
}
private function estimateSentiment(VideoMetrics $metrics): float
{
// Proxy: high like-to-view ratio with many comments = positive engagement
$likeRatio = $metrics->viewsNow > 0 ? $metrics->likes / $metrics->viewsNow : 0;
return min(100, $likeRatio * 2000);
}
}
The Result Object
<?php
class ViralityScore
{
public function __construct(
public readonly float $value,
public readonly array $components,
public readonly string $region,
) {}
public function isViral(): bool { return $this->value >= 85.0; }
public function isTrending(): bool { return $this->value >= 60.0; }
public function label(): string
{
return match(true) {
$this->value >= 85 => 'VIRAL',
$this->value >= 60 => 'TRENDING',
$this->value >= 40 => 'RISING',
default => 'NORMAL',
};
}
}
Regional Baselines Matter
Notice the REGIONAL_BASELINES array. A video getting 10,000 views/hour in Poland is proportionally more significant than the same velocity in the US. By normalizing against regional baselines, we can fairly compare viral potential across all 7 regions that ViralVidVault tracks.
Running It in Production
On viralvidvault.com, this scorer runs as part of a cron pipeline every 7 hours. Videos scoring above 85 get promoted to the homepage viral section. The regional normalization is what makes European content — Polish music clips, Dutch comedy, Scandinavian outdoor adventures — rank alongside high-view-count US content.
Key Takeaways
- Virality is relative — always normalize against baselines
- Composite scores beat single metrics — views alone miss engagement
- Regional context matters — what's viral in Norway differs from the US
- PHP 8.3 readonly properties make metric objects clean and safe
The full implementation is open source. Check the repo and give it a spin on your own video data.
This algorithm powers ViralVidVault — your vault of viral videos from around the world.
Top comments (0)