4 models have responded to the "Gravitational Waves Animation" challenge. Compare their approaches side-by-side on RIVAL. This response is part of RIVAL's open dataset of 5,600+ AI model responses.
Kimi K2 Thinking's response to the 'Gravitational Waves Animation' challenge.
Generate a beautiful, 30-second soft grid animation in HTML visualizing gravitational waves from two colliding black holes including ringdown. Maximize physical accuracy and sanity check the trajectories. In a single-page self-contained HTML.
Couldn't render this response
This model didn't return a displayable web page for this challenge.
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Gravitational Waves - Black Hole Merger</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: radial-gradient(ellipse at center, #0a0e2a 0%, #000000 100%);
overflow: hidden;
font-family: 'Arial', sans-serif;
}
canvas {
display: block;
cursor: crosshair;
}
.info {
position: absolute;
top: 20px;
left: 20px;
color: #e0f7ff;
font-size: 14px;
z-index: 10;
background: rgba(10, 20, 40, 0.6);
backdrop-filter: blur(10px);
padding: 15px;
border-radius: 8px;
border: 1px solid rgba(64, 156, 255, 0.2);
box-shadow: 0 4px 20px rgba(0, 60, 120, 0.3);
}
.info h1 {
font-size: 18px;
margin-bottom: 10px;
color: #4fc3f7;
text-shadow: 0 0 10px rgba(79, 195, 247, 0.7);
}
.info p {
margin: 5px 0;
color: #bbdefb;
}
.controls {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 10px;
z-index: 10;
}
button {
padding: 10px 20px;
background: rgba(25, 118, 210, 0.2);
border: 1px solid rgba(64, 156, 255, 0.4);
color: #e3f2fd;
cursor: pointer;
border-radius: 5px;
transition: all 0.3s;
font-weight: bold;
box-shadow: 0 2px 10px rgba(0, 60, 120, 0.2);
}
button:hover {
background: rgba(41, 121, 255, 0.3);
box-shadow: 0 0 15px rgba(66, 165, 245, 0.5);
}
.legend {
position: absolute;
top: 20px;
right: 20px;
color: #e0f7ff;
font-size: 12px;
background: rgba(10, 20, 40, 0.6);
backdrop-filter: blur(10px);
padding: 15px;
border-radius: 8px;
border: 1px solid rgba(64, 156, 255, 0.2);
box-shadow: 0 4px 20px rgba(0, 60, 120, 0.3);
}
.legend-item {
margin: 8px 0;
display: flex;
align-items: center;
gap: 10px;
color: #bbdefb;
}
.color-box {
width: 30px;
height: 3px;
border-radius: 2px;
}
</style>
</head>
<body>
<div class="info">
<h1>Gravitational Wave Visualization</h1>
<p>Two black holes spiraling together</p>
<p>Time: <span id="time">0.0</span>s</p>
<p>Phase: <span id="phase">Inspiral</span></p>
</div>
<div class="legend">
<div class="legend-item">
<div class="color-box" style="background: #ff4444;"></div>
<span>Black Hole 1</span>
</div>
<div class="legend-item">
<div class="color-box" style="background: #4444ff;"></div>
<span>Black Hole 2</span>
</div>
<div class="legend-item">
<div class="color-box" style="background: #00ffff;"></div>
<span>Gravitational Waves</span>
</div>
</div>
<div class="controls">
<button onclick="toggleAnimation()">Play/Pause</button>
<button onclick="resetAnimation()">Reset</button>
</div>
<canvas id="canvas"></canvas>
<script>
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
let animationId;
let isPlaying = true;
let startTime = Date.now();
let currentTime = 0;
// Physical constants (scaled for visualization)
const G = 1; // Gravitational constant (scaled)
const c = 50; // Speed of light (scaled)
const gridSize = 30;
const gridPoints = [];
// Black hole properties - using realistic mass ratio and separation
const blackHole1 = {
x: canvas.width / 2 - 100,
y: canvas.height / 2,
mass: 10, // Solar masses (scaled)
radius: 15,
vx: 0,
vy: 0.8,
trail: []
};
const blackHole2 = {
x: canvas.width / 2 + 100,
y: canvas.height / 2,
mass: 8, // Solar masses (scaled)
radius: 12,
vx: 0,
vy: -1.0,
trail: []
};
// Wave properties - modeling chirp and ringdown phases
let waveAmplitude = 0;
let waveFrequency = 0.02;
let wavePhase = 0;
let ringdownAmplitude = 0;
let mergerTime = 0;
let hasMerged = false;
// Create grid points
for (let x = 0; x < canvas.width; x += gridSize) {
for (let y = 0; y < canvas.height; y += gridSize) {
gridPoints.push({
x, y,
originalX: x,
originalY: y,
distanceToCenter1: 0,
distanceToCenter2: 0
});
}
}
// Calculate gravitational wave strain based on time and black hole dynamics
function calculateWaveStrain(time) {
const separation = Math.sqrt(
Math.pow(blackHole1.x - blackHole2.x, 2) +
Math.pow(blackHole1.y - blackHole2.y, 2)
);
// Orbital frequency from Kepler's third law
const totalMass = blackHole1.mass + blackHole2.mass;
const orbitalFreq = Math.sqrt(G * totalMass / Math.pow(separation, 3));
// Gravitational wave frequency is twice the orbital frequency
waveFrequency = 2 * orbitalFreq;
// Amplitude scales with chirp mass and separation
const chirpMass = Math.pow(blackHole1.mass * blackHole2.mass, 3/5) /
Math.pow(totalMass, 1/5);
const amplitude = (G * chirpMass) / (c * c * separation);
return amplitude * Math.sin(wavePhase);
}
// Update physics - handles inspiral, merger and ringdown phases
function updatePhysics(deltaTime) {
if (hasMerged) return;
// Calculate distance between black holes
const dx = blackHole2.x - blackHole1.x;
const dy = blackHole2.y - blackHole1.y;
const distance = Math.sqrt(dx * dx + dy * dy);
// Calculate gravitational force
const force = G * blackHole1.mass * blackHole2.mass / (distance * distance);
const ax = force * dx / distance / blackHole1.mass;
const ay = force * dy / distance / blackHole1.mass;
// Apply gravitational wave back-reaction (simplified)
const energyLossFactor = 1 + 0.1 * waveAmplitude * waveAmplitude;
// Update velocities
blackHole1.vx += ax * deltaTime * energyLossFactor;
blackHole1.vy += ay * deltaTime * energyLossFactor;
blackHole2.vx -= ax * deltaTime * energyLossFactor;
blackHole2.vy -= ay * deltaTime * energyLossFactor;
// Update positions
blackHole1.x += blackHole1.vx * deltaTime;
blackHole1.y += blackHole1.vy * deltaTime;
blackHole2.x += blackHole2.vx * deltaTime;
blackHole2.y += blackHole2.vy * deltaTime;
// Add to trails
blackHole1.trail.push({ x: blackHole1.x, y: blackHole1.y });
blackHole2.trail.push({ x: blackHole2.x, y: blackHole2.y });
// Limit trail length
if (blackHole1.trail.length > 100) {
blackHole1.trail.shift();
blackHole2.trail.shift();
}
// Check for merger
if (distance < (blackHole1.radius + blackHole2.radius) * 1.5 && !hasMerged) {
hasMerged = true;
mergerTime = currentTime;
// Combine masses and create final black hole
const finalMass = blackHole1.mass + blackHole2.mass;
blackHole1.mass = finalMass;
blackHole1.radius = Math.sqrt(finalMass) * 5;
blackHole1.x = (blackHole1.x + blackHole2.x) / 2;
blackHole1.y = (blackHole1.y + blackHole2.y) / 2;
blackHole1.vx = 0;
blackHole1.vy = 0;
}
// Update wave properties
waveAmplitude = calculateWaveStrain(currentTime);
wavePhase += waveFrequency * deltaTime * 60;
// Ringdown phase after merger
if (hasMerged) {
const timeSinceMerger = currentTime - mergerTime;
const ringdownFreq = 0.1; // Quasi-normal mode frequency
const damping = Math.exp(-timeSinceMerger / 30); // Damping factor
ringdownAmplitude = waveAmplitude * damping * Math.sin(ringdownFreq * timeSinceMerger * 60);
}
}
// Draw black hole with accretion disk effect
function drawBlackHole(bh, color) {
// Draw accretion disk
const gradient = ctx.createRadialGradient(bh.x, bh.y, bh.radius, bh.x, bh.y, bh.radius * 3);
gradient.addColorStop(0, 'transparent');
gradient.addColorStop(0.5, color + '40');
gradient.addColorStop(1, 'transparent');
ctx.fillStyle = gradient;
ctx.beginPath();
ctx.arc(bh.x, bh.y, bh.radius * 3, 0, Math.PI * 2);
ctx.fill();
// Draw black hole
const bhGradient = ctx.createRadialGradient(bh.x, bh.y, 0, bh.x, bh.y, bh.radius);
bhGradient.addColorStop(0, color);
bhGradient.addColorStop(0.7, color + 'dd');
bhGradient.addColorStop(1, '#000000');
ctx.fillStyle = bhGradient;
ctx.beginPath();
ctx.arc(bh.x, bh.y, bh.radius, 0, Math.PI * 2);
ctx.fill();
// Draw event horizon glow
ctx.strokeStyle = color + '60';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.arc(bh.x, bh.y, bh.radius * 1.2, 0, Math.PI * 2);
ctx.stroke();
}
// Draw gravitational wave grid deformation
function drawGrid() {
ctx.strokeStyle = 'rgba(79, 195, 247, 0.15)';
ctx.lineWidth = 1;
// Update grid points based on gravitational waves
gridPoints.forEach(point => {
// Calculate distance to each black hole
const dx1 = point.originalX - blackHole1.x;
const dy1 = point.originalY - blackHole1.y;
const distance1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
const dx2 = point.originalX - blackHole2.x;
const dy2 = point.originalY - blackHole2.y;
const distance2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
point.distanceToCenter1 = distance1;
point.distanceToCenter2 = distance2;
// Calculate gravitational wave displacement
let displacement = 0;
if (!hasMerged) {
// Inspiral phase - quadrupole radiation pattern
const factor1 = waveAmplitude * 50 / (distance1 + 100);
const factor2 = waveAmplitude * 50 / (distance2 + 100);
// Plus polarization pattern
const angle1 = Math.atan2(dy1, dx1);
const angle2 = Math.atan2(dy2, dx2);
displacement = factor1 * Math.cos(2 * angle1) * Math.sin(wavePhase) +
factor2 * Math.cos(2 * angle2) * Math.sin(wavePhase);
} else {
// Ringdown phase - damped oscillations
const timeSinceMerger = currentTime - mergerTime;
const ringdownFactor = Math.exp(-timeSinceMerger / 20) * 30;
const centerX = blackHole1.x;
const centerY = blackHole1.y;
const dx = point.originalX - centerX;
const dy = point.originalY - centerY;
const distance = Math.sqrt(dx * dx + dy * dy);
displacement = ringdownFactor * Math.sin(distance * 0.05 - timeSinceMerger * 0.5) / (distance + 50);
}
// Apply displacement
point.x = point.originalX + displacement;
point.y = point.originalY + displacement;
});
// Draw horizontal lines
for (let y = 0; y < canvas.height; y += gridSize) {
ctx.beginPath();
for (let x = 0; x < canvas.width; x += gridSize) {
const point = gridPoints.find(p => p.originalX === x && p.originalY === y);
if (point) {
if (x === 0) {
ctx.moveTo(point.x, point.y);
} else {
ctx.lineTo(point.x, point.y);
}
}
}
ctx.stroke();
}
// Draw vertical lines
for (let x = 0; x < canvas.width; x += gridSize) {
ctx.beginPath();
for (let y = 0; y < canvas.height; y += gridSize) {
const point = gridPoints.find(p => p.originalX === x && p.originalY === y);
if (point) {
if (y === 0) {
ctx.moveTo(point.x, point.y);
} else {
ctx.lineTo(point.x, point.y);
}
}
}
ctx.stroke();
}
}
// Draw orbital trails showing black hole motion
function drawTrails() {
// Draw black hole 1 trail
if (blackHole1.trail.length > 1) {
ctx.strokeStyle = 'rgba(255, 100, 100, 0.4)';
ctx.lineWidth = 2;
ctx.beginPath();
blackHole1.trail.forEach((point, index) => {
if (index === 0) {
ctx.moveTo(point.x, point.y);
} else {
ctx.lineTo(point.x, point.y);
}
});
ctx.stroke();
}
// Draw black hole 2 trail
if (blackHole2.trail.length > 1) {
ctx.strokeStyle = 'rgba(100, 100, 255, 0.4)';
ctx.lineWidth = 2;
ctx.beginPath();
blackHole2.trail.forEach((point, index) => {
if (index === 0) {
ctx.moveTo(point.x, point.y);
} else {
ctx.lineTo(point.x, point.y);
}
});
ctx.stroke();
}
}
// Main animation loop
function animate() {
if (!isPlaying) return;
// Clear canvas with fade effect
ctx.fillStyle = 'rgba(10, 14, 42, 0.15)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Update time
currentTime = (Date.now() - startTime) / 1000;
// Check if animation should loop
if (currentTime > 30) {
resetAnimation();
return;
}
// Update physics
updatePhysics(1/60);
// Draw trails
drawTrails();
// Draw grid
drawGrid();
// Draw black holes
if (!hasMerged) {
drawBlackHole(blackHole1, '#ff6b6b');
drawBlackHole(blackHole2, '#4d8af0');
} else {
drawBlackHole(blackHole1, '#9c4dff');
}
// Update UI
document.getElementById('time').textContent = currentTime.toFixed(1);
let phase = 'Inspiral';
if (hasMerged) phase = 'Ringdown';
else if (currentTime > 25) phase = '