1<!doctype html>
2<html lang="en">
3 <head>
4 <meta charset="UTF-8" />
5 <meta name="viewport" content="width=1920, height=1080" />
6 <title>Pixies launch</title>
7 <script src="assets/vendor/gsap.min.js"></script>
8 <style>
9 @font-face {
10 font-family: "Inter";
11 src: url("assets/fonts/inter-400.woff2") format("woff2");
12 font-weight: 400;
13 font-display: block;
14 }
15 @font-face {
16 font-family: "Inter";
17 src: url("assets/fonts/inter-500.woff2") format("woff2");
18 font-weight: 500;
19 font-display: block;
20 }
21 @font-face {
22 font-family: "Inter";
23 src: url("assets/fonts/inter-600.woff2") format("woff2");
24 font-weight: 600;
25 font-display: block;
26 }
27 @font-face {
28 font-family: "JetBrains Mono";
29 src: url("assets/fonts/jetbrains-mono-500.woff2") format("woff2");
30 font-weight: 500;
31 font-display: block;
32 }
33 :root {
34 --ground: #0b0f14;
35 --raised: #141a21;
36 --line: #232b34;
37 --text: #e6edf3;
38 --muted: #93a1ae;
39 --pulse: #35e0a0;
40 }
41 * {
42 margin: 0;
43 padding: 0;
44 box-sizing: border-box;
45 }
46 html,
47 body {
48 width: 1920px;
49 height: 1080px;
50 overflow: hidden;
51 background: var(--ground);
52 font-family: "Inter", system-ui, sans-serif;
53 color: var(--text);
54 -webkit-font-smoothing: antialiased;
55 }
56 #root {
57 position: relative;
58 width: 100%;
59 height: 100%;
60 overflow: hidden;
61 }
62 .scene {
63 position: absolute;
64 inset: 0;
65 background: var(--ground);
66 }
67 .mono {
68 font-family: "JetBrains Mono", ui-monospace, monospace;
69 font-weight: 500;
70 letter-spacing: 0.06em;
71 }
72 .caption {
73 position: absolute;
74 left: 140px;
75 top: 112px;
76 font-size: 44px;
77 font-weight: 500;
78 letter-spacing: -0.018em;
79 color: var(--text);
80 }
81 .brand-tag {
82 position: absolute;
83 left: 140px;
84 top: 108px;
85 font-size: 24px;
86 color: var(--muted);
87 }
88 /* scene 1 */
89 .scope {
90 position: absolute;
91 inset: 0;
92 }
93 .hook {
94 position: absolute;
95 left: 140px;
96 bottom: 150px;
97 font-size: 84px;
98 font-weight: 600;
99 letter-spacing: -0.032em;
100 line-height: 1.1;
101 }
102 .hook em {
103 font-style: normal;
104 color: var(--pulse);
105 }
106 /* scene 2 */
107 .s2-mark {
108 position: absolute;
109 left: 140px;
110 top: 300px;
111 width: 640px;
112 }
113 .s2-copy {
114 position: absolute;
115 left: 1040px;
116 top: 336px;
117 width: 740px;
118 }
119 .s2-copy h2 {
120 font-size: 66px;
121 font-weight: 600;
122 letter-spacing: -0.03em;
123 line-height: 1.1;
124 }
125 .s2-copy p {
126 margin-top: 28px;
127 font-size: 32px;
128 line-height: 1.5;
129 color: var(--muted);
130 }
131 .s2-rule {
132 position: absolute;
133 left: 1040px;
134 top: 300px;
135 width: 96px;
136 height: 4px;
137 background: var(--pulse);
138 border-radius: 2px;
139 opacity: 0;
140 }
141 /* panel */
142 .panel {
143 position: absolute;
144 background: var(--raised);
145 border: 1px solid var(--line);
146 border-radius: 24px;
147 }
148 .s3-panel {
149 left: 140px;
150 top: 220px;
151 width: 1640px;
152 height: 700px;
153 }
154 .s3-head {
155 position: absolute;
156 left: 60px;
157 top: 44px;
158 right: 60px;
159 display: flex;
160 justify-content: space-between;
161 font-size: 28px;
162 color: var(--muted);
163 }
164 .chart {
165 position: absolute;
166 left: 60px;
167 top: 100px;
168 width: 1520px;
169 height: 360px;
170 }
171 .s3-stats {
172 position: absolute;
173 left: 60px;
174 top: 500px;
175 right: 60px;
176 display: grid;
177 grid-template-columns: repeat(4, 1fr);
178 gap: 24px;
179 }
180 .stat {
181 padding-top: 26px;
182 border-top: 1px solid var(--line);
183 }
184 .stat .label {
185 font-size: 28px;
186 color: var(--muted);
187 letter-spacing: 0.1em;
188 }
189 .stat .value {
190 margin-top: 12px;
191 font-size: 54px;
192 letter-spacing: -0.01em;
193 }
194 .stat .value span {
195 font-size: 28px;
196 color: var(--muted);
197 margin-left: 8px;
198 }
199 /* scene 4 */
200 .cards {
201 position: absolute;
202 left: 140px;
203 top: 340px;
204 width: 1640px;
205 display: grid;
206 grid-template-columns: repeat(3, 1fr);
207 gap: 40px;
208 }
209 .card {
210 height: 320px;
211 border-radius: 20px;
212 padding: 40px;
213 background: var(--raised);
214 border: 1px solid var(--line);
215 }
216 .card .name {
217 font-size: 28px;
218 color: var(--muted);
219 letter-spacing: 0.12em;
220 }
221 .card .ping {
222 margin-top: 56px;
223 font-size: 76px;
224 letter-spacing: -0.02em;
225 }
226 .card .ping span {
227 font-size: 28px;
228 color: var(--muted);
229 margin-left: 10px;
230 }
231 .card .sub {
232 margin-top: 10px;
233 font-size: 28px;
234 color: var(--muted);
235 }
236 .card .tick {
237 display: inline-block;
238 width: 10px;
239 height: 10px;
240 border-radius: 3px;
241 background: var(--pulse);
242 margin-right: 12px;
243 vertical-align: 2px;
244 }
245 /* scene 5 */
246 .s5-alert {
247 left: 140px;
248 top: 300px;
249 width: 900px;
250 height: 420px;
251 padding: 44px;
252 }
253 .tag {
254 display: inline-block;
255 font-size: 22px;
256 color: var(--pulse);
257 border: 1px solid #1f5a48;
258 background: rgba(53, 224, 160, 0.08);
259 border-radius: 999px;
260 padding: 8px 18px;
261 letter-spacing: 0.14em;
262 }
263 .s5-alert h3 {
264 margin-top: 28px;
265 font-size: 56px;
266 font-weight: 600;
267 letter-spacing: -0.03em;
268 }
269 .s5-alert p {
270 margin-top: 16px;
271 font-size: 28px;
272 color: var(--muted);
273 }
274 .chips {
275 margin-top: 34px;
276 display: flex;
277 gap: 12px;
278 flex-wrap: wrap;
279 }
280 .chip {
281 font-size: 28px;
282 color: var(--text);
283 border: 1px solid var(--line);
284 border-radius: 10px;
285 padding: 12px 18px;
286 letter-spacing: 0.1em;
287 background: #10151b;
288 }
289 .s5-metrics {
290 left: 1080px;
291 top: 300px;
292 width: 700px;
293 height: 420px;
294 padding: 44px;
295 }
296 .s5-metrics h3 {
297 margin-top: 26px;
298 font-size: 40px;
299 font-weight: 600;
300 letter-spacing: -0.02em;
301 line-height: 1.2;
302 }
303 .s5-metrics p {
304 margin-top: 14px;
305 font-size: 28px;
306 color: var(--muted);
307 line-height: 1.5;
308 }
309 .s5-metrics .rule {
310 margin-top: 34px;
311 height: 1px;
312 background: var(--line);
313 }
314 .s5-metrics .foot {
315 margin-top: 26px;
316 font-size: 20px;
317 color: var(--muted);
318 }
319 /* scene 6 */
320 .s6-wrap {
321 position: absolute;
322 left: 0;
323 top: 0;
324 width: 1920px;
325 height: 100%;
326 display: flex;
327 flex-direction: column;
328 align-items: center;
329 justify-content: center;
330 gap: 34px;
331 }
332 .s6-mark {
333 width: 760px;
334 }
335 .s6-tag {
336 font-size: 38px;
337 color: var(--muted);
338 letter-spacing: -0.01em;
339 }
340 .s6-url {
341 font-size: 28px;
342 color: var(--pulse);
343 }
344 </style>
345 </head>
346 <body>
347 <div
348 id="root"
349 data-composition-id="main"
350 data-start="0"
351 data-duration="20.5"
352 data-width="1920"
353 data-height="1080"
354 data-fps="30"
355 >
356 <section id="s1" class="clip scene" data-start="0" data-duration="2.735" data-track-index="0">
357 <svg class="scope" viewBox="0 0 1920 1080" aria-hidden="true">
358 <path
359 id="s1-pulse"
360 d="M120 560 H520 L600 320 L700 760 L800 420 H1240 L1330 560 H1800"
361 fill="none"
362 stroke="#35e0a0"
363 stroke-width="8"
364 stroke-linecap="round"
365 stroke-linejoin="round"
366 />
367 <circle id="s1-dot" cx="1800" cy="560" r="12" fill="#35e0a0" opacity="0" />
368 </svg>
369 <span class="brand-tag mono" data-layout-ignore>PIXIES</span>
370 <p class="hook" id="s1-line">Your line has a history.</p>
371 </section>
372
373 <section id="s2" class="clip scene" data-start="2.735" data-duration="3.83" data-track-index="0">
374 <img id="s2-wm" class="s2-mark" src="assets/brand/wordmark-dark.svg" alt="Pixies" />
375 <div class="s2-rule" id="s2-rule"></div>
376 <div class="s2-copy">
377 <h2 id="s2-h">Self-hosted internet speed analytics.</h2>
378 <p id="s2-p">Records your internet speed over a configurable retention period.</p>
379 </div>
380 </section>
381
382 <section id="s3" class="clip scene" data-start="6.565" data-duration="3.8" data-track-index="0">
383 <p class="caption" id="s3-cap">Speed, ping and jitter, recorded on a schedule.</p>
384 <div class="panel s3-panel" id="s3-panel">
385 <div class="s3-head mono">
386 <span>SPEED HISTORY</span>
387 <span>LAST 30 DAYS</span>
388 </div>
389 <svg class="chart" viewBox="0 0 1520 360" aria-hidden="true">
390 <g stroke="#232b34" stroke-width="1">
391 <line x1="60" y1="30" x2="1460" y2="30" />
392 <line x1="60" y1="120" x2="1460" y2="120" />
393 <line x1="60" y1="210" x2="1460" y2="210" />
394 <line x1="60" y1="300" x2="1460" y2="300" />
395 </g>
396 <path id="s3-area" d="M60.0 175.3 L108.3 152.1 L156.6 131.8 L204.8 141.5 L253.1 121.2 L301.4 134.2 L349.7 103.5 L397.9 109.3 L446.2 89.9 L494.5 106.2 L542.8 117.9 L591.0 95.3 L639.3 80.6 L687.6 87.9 L735.9 106.2 L784.1 75.5 L832.4 82.6 L880.7 66.1 L929.0 81.1 L977.2 96.1 L1025.5 72.3 L1073.8 60.0 L1122.1 76.8 L1170.3 57.8 L1218.6 69.6 L1266.9 48.5 L1315.2 63.0 L1363.4 82.6 L1411.7 62.5 L1460.0 49.2 L1460 360 L60 360 Z" fill="#35e0a0" opacity="0" />
397 <path
398 id="s3-line"
399 d="M60.0 175.3 L108.3 152.1 L156.6 131.8 L204.8 141.5 L253.1 121.2 L301.4 134.2 L349.7 103.5 L397.9 109.3 L446.2 89.9 L494.5 106.2 L542.8 117.9 L591.0 95.3 L639.3 80.6 L687.6 87.9 L735.9 106.2 L784.1 75.5 L832.4 82.6 L880.7 66.1 L929.0 81.1 L977.2 96.1 L1025.5 72.3 L1073.8 60.0 L1122.1 76.8 L1170.3 57.8 L1218.6 69.6 L1266.9 48.5 L1315.2 63.0 L1363.4 82.6 L1411.7 62.5 L1460.0 49.2"
400 fill="none"
401 stroke="#35e0a0"
402 stroke-width="5"
403 stroke-linecap="round"
404 stroke-linejoin="round"
405 />
406 <path
407 id="s3-ping"
408 d="M60.0 252.7 L108.3 275.3 L156.6 275.3 L204.8 232.0 L253.1 252.7 L301.4 275.3 L349.7 252.7 L397.9 209.3 L446.2 252.7 L494.5 275.3 L542.8 275.3 L591.0 252.7 L639.3 161.3 L687.6 145.2 L735.9 209.3 L784.1 252.7 L832.4 275.3 L880.7 275.3 L929.0 252.7 L977.2 232.0 L1025.5 275.3 L1073.8 275.3 L1122.1 252.7 L1170.3 275.3 L1218.6 275.3 L1266.9 252.7 L1315.2 275.3 L1363.4 252.7 L1411.7 275.3 L1460.0 275.3"
409 fill="none"
410 stroke="#93a1ae"
411 stroke-width="2"
412 stroke-dasharray="6 8"
413 opacity="0"
414 />
415 </svg>
416 <div class="s3-stats">
417 <div class="stat" id="s3-st1">
418 <div class="label mono">DOWNLOAD</div>
419 <div class="value mono">942.6<span>Mbps</span></div>
420 </div>
421 <div class="stat" id="s3-st2">
422 <div class="label mono">UPLOAD</div>
423 <div class="value mono">51.4<span>Mbps</span></div>
424 </div>
425 <div class="stat" id="s3-st3">
426 <div class="label mono">PING</div>
427 <div class="value mono">8<span>ms</span></div>
428 </div>
429 <div class="stat" id="s3-st4">
430 <div class="label mono">JITTER</div>
431 <div class="value mono">0.7<span>ms</span></div>
432 </div>
433 </div>
434 </div>
435 </section>
436
437 <section id="s4" class="clip scene" data-start="10.365" data-duration="3.83" data-track-index="0">
438 <p class="caption" id="s4-cap">Bring your own servers.</p>
439 <div class="cards">
440 <div class="card" id="s4-c1">
441 <div class="name mono">OOKLA</div>
442 <div class="ping mono">12<span>ms</span></div>
443 <div class="sub"><span class="tick"></span>median latency</div>
444 </div>
445 <div class="card" id="s4-c2">
446 <div class="name mono">LIBRESPEED</div>
447 <div class="ping mono">18<span>ms</span></div>
448 <div class="sub"><span class="tick"></span>median latency</div>
449 </div>
450 <div class="card" id="s4-c3">
451 <div class="name mono">CLOUDFLARE</div>
452 <div class="ping mono">9<span>ms</span></div>
453 <div class="sub"><span class="tick"></span>median latency</div>
454 </div>
455 </div>
456 </section>
457
458 <section id="s5" class="clip scene" data-start="14.195" data-duration="3.28" data-track-index="0">
459 <p class="caption" id="s5-cap">Know before your line drops.</p>
460 <div class="panel s5-alert" id="s5-a">
461 <span class="tag mono" data-layout-ignore>HEALTH CHECK</span>
462 <h3>Line degraded</h3>
463 <p>Download under 50 Mbps across three consecutive tests</p>
464 <div class="chips">
465 <span class="chip mono" id="s5-ch1">EMAIL</span>
466 <span class="chip mono" id="s5-ch2">SIGNAL</span>
467 <span class="chip mono" id="s5-ch3">WHATSAPP</span>
468 <span class="chip mono" id="s5-ch4">TELEGRAM</span>
469 </div>
470 </div>
471 <div class="panel s5-metrics" id="s5-m">
472 <span class="tag mono" data-layout-ignore>INTEGRATIONS</span>
473 <h3>Prometheus and Grafana</h3>
474 <p>Scrape Pixies metrics into the dashboards your team already watches.</p>
475 <div class="rule"></div>
476 <div class="foot mono" data-layout-ignore>CSV AND JSON EXPORT</div>
477 </div>
478 </section>
479
480 <section id="s6" class="clip scene" data-start="17.475" data-duration="3.025" data-track-index="0">
481 <div class="s6-wrap">
482 <img id="s6-wm" class="s6-mark" src="assets/brand/wordmark-dark.svg" alt="Pixies" />
483 <p id="s6-tag" class="s6-tag">Self-hosted speed analytics.</p>
484 <p id="s6-url" class="s6-url mono">Open source on GitHub</p>
485 </div>
486 </section>
487
488 <audio
489 id="music"
490 data-timeline-role="music"
491 data-start="0"
492 data-duration="20.5"
493 data-track-index="8"
494 src="assets/music/pulse.mp3"
495 data-automation='{"version":1,"lanes":[{"target":"volume","points":[{"t":0,"v":0},{"t":0.8,"v":0.34},{"t":19.0,"v":0.34},{"t":20.4,"v":0}]}]}'
496 ></audio>
497 <audio id="sfx-panel" data-start="6.67" data-duration="0.31" data-track-index="9" data-volume="0.12" src="assets/sfx/switch1.ogg"></audio>
498 <audio id="sfx-cards" data-start="10.57" data-duration="0.31" data-track-index="10" data-volume="0.12" src="assets/sfx/switch1.ogg"></audio>
499 <audio id="sfx-alert" data-start="14.33" data-duration="0.31" data-track-index="11" data-volume="0.12" src="assets/sfx/switch1.ogg"></audio>
500 </div>
501 <script>
502 const pulse = document.getElementById("s1-pulse");
503 const len = pulse.getTotalLength();
504 pulse.style.strokeDasharray = String(len);
505 gsap.set(pulse, { strokeDashoffset: len });
506
507 const line = document.getElementById("s3-line");
508 const lineLen = line.getTotalLength();
509 line.style.strokeDasharray = String(lineLen);
510 gsap.set(line, { strokeDashoffset: lineLen });
511
512 const tl = gsap.timeline({ paused: true });
513 const add = (target, from, to, at) => tl.fromTo(target, from, to, at);
514
515 // scene 1 — hook (0.0s, beat-locked 0.00)
516 add("#s1-pulse", { strokeDashoffset: len }, { strokeDashoffset: 0, duration: 1.15, ease: "power2.inOut" }, 0.1);
517 add("#s1-dot", { opacity: 0, scale: 0.4 }, { opacity: 1, scale: 1, duration: 0.35, ease: "back.out(2)" }, 1.05);
518 add("#s1-line", { opacity: 0, y: 26 }, { opacity: 1, y: 0, duration: 0.7, ease: "power3.out" }, 1.0);
519 add(".brand-tag", { opacity: 0 }, { opacity: 1, duration: 0.4 }, 0.25);
520
521 // scene 2 — reveal (2.8s)
522 add(".s2-mark", { opacity: 0, x: -34 }, { opacity: 1, x: 0, duration: 0.75, ease: "power3.out" }, 2.85);
523 add(".s2-rule", { opacity: 0, scaleX: 0.2 }, { opacity: 1, scaleX: 1, duration: 0.5, ease: "power2.out" }, 3.15);
524 add("#s2-h", { opacity: 0, y: 28 }, { opacity: 1, y: 0, duration: 0.7, ease: "power3.out" }, 3.3);
525 add("#s2-p", { opacity: 0, y: 22 }, { opacity: 1, y: 0, duration: 0.7, ease: "power3.out" }, 3.6);
526
527 // scene 3 — speed history (6.4s, beat-locked 6.40)
528 add("#s3-cap", { opacity: 0, y: 20 }, { opacity: 1, y: 0, duration: 0.6, ease: "power3.out" }, 6.62);
529 add("#s3-panel", { opacity: 0, y: 26 }, { opacity: 1, y: 0, duration: 0.7, ease: "power3.out" }, 6.72);
530 add(".s3-head", { opacity: 0 }, { opacity: 1, duration: 0.5 }, 7.05);
531 add("#s3-line", { strokeDashoffset: lineLen }, { strokeDashoffset: 0, duration: 1.1, ease: "power2.inOut" }, 7.05);
532 add("#s3-area", { opacity: 0 }, { opacity: 0.12, duration: 0.7 }, 7.45);
533 add("#s3-ping", { opacity: 0 }, { opacity: 0.65, duration: 0.6 }, 7.58);
534 ["#s3-st1", "#s3-st2", "#s3-st3", "#s3-st4"].forEach((sel, i) => {
535 add(sel, { opacity: 0, y: 18 }, { opacity: 1, y: 0, duration: 0.5, ease: "power2.out" }, 8.0 + i * 0.16);
536 });
537
538 // scene 4 — servers (10.4s)
539 add("#s4-cap", { opacity: 0, y: 20 }, { opacity: 1, y: 0, duration: 0.6, ease: "power3.out" }, 10.45);
540 ["#s4-c1", "#s4-c2", "#s4-c3"].forEach((sel, i) => {
541 add(sel, { opacity: 0, y: 30 }, { opacity: 1, y: 0, duration: 0.55, ease: "power3.out" }, 10.58 + i * 0.16);
542 });
543
544 // scene 5 — health checks (14.0s)
545 add("#s5-cap", { opacity: 0, y: 20 }, { opacity: 1, y: 0, duration: 0.6, ease: "power3.out" }, 14.25);
546 add("#s5-a", { opacity: 0, y: 26 }, { opacity: 1, y: 0, duration: 0.6, ease: "power3.out" }, 14.35);
547 add("#s5-m", { opacity: 0, y: 26 }, { opacity: 1, y: 0, duration: 0.6, ease: "power3.out" }, 14.5);
548 ["#s5-ch1", "#s5-ch2", "#s5-ch3", "#s5-ch4"].forEach((sel, i) => {
549 add(sel, { opacity: 0, y: 14 }, { opacity: 1, y: 0, duration: 0.35, ease: "power2.out" }, 14.72 + i * 0.12);
550 });
551
552 // scene 6 — outro (17.2s, beat-locked 17.20)
553 add("#s6-wm", { opacity: 0, scale: 0.97 }, { opacity: 1, scale: 1, duration: 0.8, ease: "power3.out" }, 17.55);
554 add("#s6-tag", { opacity: 0, y: 16 }, { opacity: 1, y: 0, duration: 0.6, ease: "power2.out" }, 17.75);
555 add("#s6-url", { opacity: 0 }, { opacity: 1, duration: 0.5 }, 18.0);
556
557 window.__timelines["main"] = tl;
558 tl.seek(0);
559 </script>
560 </body>
561</html>
562