Ceramic whale
A cobalt hero with a giant cream DEPTH wordmark where a frame-animated ceramic whale, rippled with a travelling sine warp, swims right to left between the letters while bubbles drift upward.
Assets
Code: MIT license. Imagery generated with Google Gemini · Video generated with Google Veo
<!doctype html>
<html lang="en">
<head
><script>
/* 6to9 pattern harness */ (function () {
var parentPaused = false,
hidden = document.hidden,
p = (window.__pattern = { paused: false, reducedMotion: false, maxDpr: 2 });
function sync() {
p.paused = parentPaused || hidden;
}
sync();
try {
p.reducedMotion = matchMedia("(prefers-reduced-motion: reduce)").matches;
} catch (e) {}
document.addEventListener("visibilitychange", function () {
hidden = document.hidden;
sync();
});
addEventListener("message", function (e) {
var d = e.data || {};
if (d.pattern === "pause") {
parentPaused = true;
sync();
}
if (d.pattern === "play") {
parentPaused = false;
sync();
}
});
})();
</script>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>DEPTH - Ceramic whale</title>
<style>
:root {
--cobalt: #1240d0;
--cream: #f6ecd6;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html,
body {
width: 100%;
height: 100%;
overflow: hidden;
background: var(--cobalt);
}
.hero {
position: relative;
width: 100vw;
height: 100vh;
overflow: hidden;
background: var(--cobalt);
color: var(--cream);
font-family: -apple-system, "Segoe UI", Helvetica, Arial, sans-serif;
}
canvas {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
display: block;
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip: rect(0 0 0 0);
white-space: nowrap;
}
.nav {
position: absolute;
top: 28px;
left: 4vw;
right: 4vw;
display: flex;
justify-content: space-between;
z-index: 2;
font-size: 13px;
font-weight: 700;
letter-spacing: 0.16em;
text-transform: uppercase;
}
.copy {
position: absolute;
left: 4vw;
right: 4vw;
bottom: 5vh;
display: flex;
justify-content: space-between;
align-items: flex-end;
gap: 24px;
z-index: 2;
}
.copy p {
max-width: 30ch;
font-size: clamp(15px, 1.4vw, 20px);
line-height: 1.45;
}
.cta {
display: inline-block;
padding: 14px 28px;
border-radius: 999px;
background: var(--cream);
color: var(--cobalt);
font-weight: 800;
font-size: 15px;
letter-spacing: 0.04em;
text-decoration: none;
white-space: nowrap;
}
.fallback {
display: none;
position: absolute;
inset: 0;
align-items: center;
justify-content: center;
font-weight: 900;
font-size: 26vw;
letter-spacing: -0.04em;
color: var(--cream);
background: radial-gradient(ellipse at 70% 40%, #2a5cf0, var(--cobalt) 60%);
}
.no-canvas .fallback {
display: flex;
}
@media (max-width: 640px) {
.copy {
flex-direction: column;
align-items: flex-start;
bottom: 6vh;
}
}
</style>
</head>
<body>
<section class="hero" id="hero">
<canvas
id="stage"
aria-label="A glazed ceramic whale swimming across the word DEPTH"
></canvas>
<div class="fallback" aria-hidden="true">DEPTH</div>
<h1 class="sr-only">DEPTH</h1>
<div class="nav">
<span>Kiln & Tide</span>
<span>Vol. 04</span>
</div>
<div class="copy">
<p>Hand-glazed, slow-fired, made to drift. Sink into something quiet.</p>
<a class="cta" href="#">Explore the collection</a>
</div>
</section>
<script>
(function () {
var P = window.__pattern || { paused: false, reducedMotion: false, maxDpr: 2 };
var hero = document.getElementById("hero");
var canvas = document.getElementById("stage");
var ctx = canvas.getContext("2d");
if (!ctx) {
hero.classList.add("no-canvas");
return;
}
var WORD = "DEPTH";
var FRONT = [0, 2, 4];
var CREAM = "#f6ecd6";
var COBALT = "#1240d0";
var CELL_W = 480;
var CELL_H = 216;
var COLS = 5;
var FRAMES = 48;
var FPS = 24;
var TRAVEL_SECONDS = 34;
var STRIPS = 54;
var FACES_LEFT = true;
var STILL_TIME = 17;
var atlas = new Image();
var atlasReady = false;
var dirty = true;
atlas.crossOrigin = "anonymous";
atlas.onload = function () {
atlasReady = true;
dirty = true;
};
atlas.onerror = function () {
atlasReady = false;
};
atlas.src = "https://agents-dev.6to9.ai/visuals/07cc6d0b5ae16ac0c505c3763c8d15af.webp";
var W = 0;
var H = 0;
var dpr = 1;
var layout = null;
var bubbles = [];
function seeded(seed) {
var s = seed;
return function () {
s = (s * 16807) % 2147483647;
return (s - 1) / 2147483646;
};
}
function resize() {
dpr = Math.max(1, Math.min(window.devicePixelRatio || 1, P.maxDpr || 2));
W = hero.clientWidth;
H = hero.clientHeight;
canvas.width = Math.round(W * dpr);
canvas.height = Math.round(H * dpr);
buildLayout();
buildBubbles();
dirty = true;
}
function buildLayout() {
var portrait = W / H < 0.8;
var yScale = portrait ? 2.2 : 1;
var targetW = W * 0.94;
var track = -0.03;
ctx.font = '900 100px Impact, "Arial Black", "Helvetica Neue", Arial, sans-serif';
var widths = [];
var total = 0;
for (var i = 0; i < WORD.length; i++) {
var w = ctx.measureText(WORD[i]).width;
widths.push(w);
total += w;
if (i < WORD.length - 1) total += track * 100;
}
var fs = (targetW / total) * 100;
var k = fs / 100;
var m = ctx.measureText("H");
var cap = (m.actualBoundingBoxAscent || 72) * k;
var typeCy = H * (portrait ? 0.4 : 0.46);
var x = (W - targetW) / 2;
var letters = [];
for (var j = 0; j < WORD.length; j++) {
letters.push({ ch: WORD[j], x: x });
x += widths[j] * k + track * fs;
}
layout = {
fs: fs,
yScale: yScale,
cap: cap,
typeCy: typeCy,
baseline: typeCy + (cap * yScale) / 2,
letters: letters,
whaleW: portrait ? W * 0.96 : Math.min(W * 0.64, H * 1.5),
};
}
function buildBubbles() {
var rnd = seeded(42);
bubbles = [];
var count = W < 640 ? 9 : 16;
for (var i = 0; i < count; i++) {
bubbles.push({
x: rnd(),
y0: rnd() * (H + 80),
r: 4 + rnd() * 16,
v: 14 + rnd() * 26,
w: 0.4 + rnd() * 0.8,
ph: rnd() * 6.28,
front: i % 4 === 0,
});
}
}
function drawBubbles(t, front) {
for (var i = 0; i < bubbles.length; i++) {
var b = bubbles[i];
if (b.front !== front) continue;
var y = H + 40 - ((b.y0 + t * b.v) % (H + 80));
var x = b.x * W + Math.sin(t * b.w + b.ph) * 12;
var g = ctx.createRadialGradient(x - b.r * 0.3, y - b.r * 0.3, b.r * 0.1, x, y, b.r);
g.addColorStop(0, "rgba(255,255,255,0.28)");
g.addColorStop(1, "rgba(255,255,255,0.04)");
ctx.beginPath();
ctx.arc(x, y, b.r, 0, Math.PI * 2);
ctx.fillStyle = g;
ctx.fill();
ctx.lineWidth = 1.5;
ctx.strokeStyle = "rgba(246,236,214,0.6)";
ctx.stroke();
ctx.beginPath();
ctx.arc(x - b.r * 0.35, y - b.r * 0.35, b.r * 0.18, 0, Math.PI * 2);
ctx.fillStyle = "rgba(255,255,255,0.7)";
ctx.fill();
}
}
function drawLetters(indices, shadow) {
ctx.font =
"900 " + layout.fs + 'px Impact, "Arial Black", "Helvetica Neue", Arial, sans-serif';
ctx.textBaseline = "alphabetic";
ctx.fillStyle = CREAM;
for (var n = 0; n < indices.length; n++) {
var L = layout.letters[indices[n]];
ctx.save();
if (shadow) {
ctx.shadowColor = "rgba(0,10,70,0.4)";
ctx.shadowBlur = 36;
ctx.shadowOffsetY = 10;
}
ctx.translate(L.x, layout.baseline);
ctx.scale(1, layout.yScale);
ctx.fillText(L.ch, 0, 0);
ctx.restore();
}
}
// Mesh warp: the cell is cut into horizontal strips, each offset by a travelling sine.
function drawWhaleFrame(index, alpha, t, ww, wh) {
var sx = (index % COLS) * CELL_W;
var sy = Math.floor(index / COLS) * CELL_H;
var srcStrip = (CELL_H - 1) / STRIPS;
var dstStrip = wh / STRIPS;
var amp = ww * 0.01;
ctx.globalAlpha = alpha;
for (var k = 0; k < STRIPS; k++) {
var v = (k + 0.5) / STRIPS;
var off = amp * Math.sin(v * 6.0 - t * 1.6) * (0.4 + 0.6 * Math.sin(v * Math.PI));
ctx.drawImage(
atlas,
sx + 0.5,
sy + 0.5 + k * srcStrip,
CELL_W - 1,
srcStrip,
-ww / 2 + off,
-wh / 2 + k * dstStrip,
ww,
dstStrip + 0.7,
);
}
ctx.globalAlpha = 1;
}
function drawWhale(t) {
if (!atlasReady) return;
var ww = layout.whaleW;
var wh = (ww * CELL_H) / CELL_W;
var p = (t / TRAVEL_SECONDS) % 1;
var cx = W + ww * 0.4 - p * (W + ww * 0.8);
var cy = layout.typeCy + H * 0.02 + Math.sin(t * 0.5) * H * 0.018;
var f = (t * FPS) % FRAMES;
var i = Math.floor(f);
var j = (i + 1) % FRAMES;
var a = f - i;
ctx.save();
ctx.translate(cx, cy);
ctx.rotate(Math.sin(t * 0.4) * 0.025);
if (!FACES_LEFT) ctx.scale(-1, 1);
drawWhaleFrame(i, 1, t, ww, wh);
drawWhaleFrame(j, a, t, ww, wh);
ctx.restore();
}
function draw(t) {
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
ctx.globalAlpha = 1;
ctx.fillStyle = COBALT;
ctx.fillRect(0, 0, W, H);
drawBubbles(t, false);
drawLetters([0, 1, 2, 3, 4], false);
drawWhale(t);
drawLetters(FRONT, true);
drawBubbles(t, true);
}
var clock = 0;
var last = performance.now();
function frame(now) {
var dt = Math.min((now - last) / 1000, 0.1);
last = now;
if (P.reducedMotion) {
if (dirty) {
draw(STILL_TIME);
dirty = false;
}
} else if (!P.paused) {
clock += dt;
draw(clock);
dirty = false;
} else if (dirty) {
draw(clock);
dirty = false;
}
requestAnimationFrame(frame);
}
window.addEventListener("resize", resize);
resize();
requestAnimationFrame(frame);
})();
</script>
</body>
</html>
Made by 6to9. Back to all motion patterns.