1<!DOCTYPE html>
2<html lang="en">
3<head>
4<meta charset="UTF-8">
5<meta name="viewport" content="width=device-width, initial-scale=1.0">
6<title>Blob Animation</title>
7<style>
8 * { margin: 0; padding: 0; box-sizing: border-box; }
9
10 body {
11 background: #000;
12 width: 100vw;
13 height: 100vh;
14 display: flex;
15 align-items: center;
16 justify-content: center;
17 overflow: hidden;
18 }
19
20 .container {
21 position: relative;
22 width: 100px;
23 height: 110px;
24 /* Slightly left of center based on frames */
25 transform: translate(-10px, 10px);
26 }
27
28 .blob {
29 position: absolute;
30 width: 100%;
31 height: 100%;
32 background: white;
33 animation: morph 1.5s ease-in-out forwards;
34 }
35
36 @keyframes morph {
37 0% {
38 /* Round oval - equal border radius */
39 border-radius: 50% 50% 50% 50% / 55% 55% 45% 45%;
40 transform: scaleX(1) scaleY(1);
41 }
42 15% {
43 /* Transitioning to head shape */
44 border-radius: 50% 50% 45% 45% / 55% 55% 48% 48%;
45 }
46 30% {
47 /* Head shape - wider top, narrower chin */
48 border-radius: 50% 50% 40% 40% / 58% 58% 42% 42%;
49 }
50 50% {
51 /* Settled head shape */
52 border-radius: 48% 48% 38% 38% / 58% 58% 42% 42%;
53 }
54 100% {
55 /* Final head/face silhouette shape */
56 border-radius: 48% 48% 38% 38% / 58% 58% 42% 42%;
57 }
58 }
59</style>
60</head>
61<body>
62 <div class="container">
63 <div class="blob"></div>
64 </div>
65
66 <script>
67 // Use SVG/canvas for more precise blob morphing
68 const canvas = document.createElement('canvas');
69 canvas.width = window.innerWidth;
70 canvas.height = window.innerHeight;
71 canvas.style.position = 'fixed';
72 canvas.style.top = '0';
73 canvas.style.left = '0';
74 document.body.innerHTML = '';
75 document.body.style.background = '#000';
76 document.body.appendChild(canvas);
77
78 const ctx = canvas.getContext('2d');
79 const cx = canvas.width / 2 - 10;
80 const cy = canvas.height / 2 + 10;
81
82 const startTime = performance.now();
83 const duration = 600; // ms for transition
84
85 function lerp(a, b, t) {
86 return a + (b - a) * t;
87 }
88
89 function easeOut(t) {
90 return 1 - Math.pow(1 - t, 3);
91 }
92
93 // Draw a blob shape using bezier curves
94 // Shape 1: oval (symmetric)
95 // Shape 2: head silhouette (wider top, narrower chin area)
96 function drawBlob(t) {
97 ctx.clearRect(0, 0, canvas.width, canvas.height);
98 ctx.fillStyle = 'white';
99 ctx.beginPath();
100
101 const w = lerp(52, 58, t); // half-width
102 const hTop = lerp(58, 62, t); // half-height top
103 const hBot = lerp(58, 52, t); // half-height bottom
104
105 // Control point adjustments for chin narrowing
106 const chinNarrow = lerp(0, 10, t); // how much chin narrows
107 const topWiden = lerp(0, 4, t); // how much top widens
108
109 // Draw using bezier curves: top, right, bottom, left
110 // Starting from top center
111 ctx.moveTo(cx, cy - hTop);
112
113 // Top-right curve
114 ctx.bezierCurveTo(
115 cx + w * 0.6 + topWiden, cy - hTop,
116 cx + w, cy - hTop * 0.4,
117 cx + w, cy
118 );
119
120 // Bottom-right curve (chin area)
121 ctx.bezierCurveTo(
122 cx + w, cy + hBot * 0.5,
123 cx + w * 0.5 - chinNarrow * 0.3, cy + hBot,
124 cx, cy + hBot
125 );
126
127 // Bottom-left curve (chin area)
128 ctx.bezierCurveTo(
129 cx - w * 0.5 + chinNarrow * 0.3, cy + hBot,
130 cx - w, cy + hBot * 0.5,
131 cx - w, cy
132 );
133
134 // Top-left curve
135 ctx.bezierCurveTo(
136 cx - w, cy - hTop * 0.4,
137 cx - w * 0.6 - topWiden, cy - hTop,
138 cx, cy - hTop
139 );
140
141 ctx.closePath();
142 ctx.fill();
143 }
144
145 function animate(now) {
146 const elapsed = now - startTime;
147 const rawT = Math.min(elapsed / duration, 1);
148 const t = easeOut(rawT);
149
150 drawBlob(t);
151
152 if (rawT < 1) {
153 requestAnimationFrame(animate);
154 } else {
155 // Keep drawing final state
156 drawBlob(1);
157 }
158 }
159
160 requestAnimationFrame(animate);
161 </script>
162</body>
163</html>