← back to Houdini Gradient Border Buttoncompare with ground truth →
model output
Claude Sonnet 4.6
Houdini Gradient Border Button
A 0.93T 0.27
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>Shiny Border</title>
7<style>
8 * {
9 margin: 0;
10 padding: 0;
11 box-sizing: border-box;
12 }
13
14 body {
15 background: #000;
16 display: flex;
17 justify-content: center;
18 align-items: center;
19 min-height: 100vh;
20 font-family: Arial, sans-serif;
21 }
22
23 .btn-wrapper {
24 position: relative;
25 padding: 2px;
26 background: transparent;
27 }
28
29 .btn {
30 position: relative;
31 padding: 12px 30px;
32 background: #000;
33 color: #fff;
34 font-size: 14px;
35 letter-spacing: 0.5px;
36 border: none;
37 cursor: pointer;
38 display: block;
39 z-index: 1;
40 }
41
42 .border-canvas {
43 position: absolute;
44 top: 0;
45 left: 0;
46 width: 100%;
47 height: 100%;
48 pointer-events: none;
49 }
50</style>
51</head>
52<body>
53
54<div class="btn-wrapper">
55 <canvas class="border-canvas" id="borderCanvas"></canvas>
56 <button class="btn">Shiny Border</button>
57</div>
58
59<script>
60 const canvas = document.getElementById('borderCanvas');
61 const wrapper = canvas.parentElement;
62
63 function resize() {
64 canvas.width = wrapper.offsetWidth;
65 canvas.height = wrapper.offsetHeight;
66 }
67 resize();
68
69 const ctx = canvas.getContext('2d');
70
71 const duration = 3000; // ms for one full loop
72 let startTime = null;
73
74 function getPerimeter(w, h) {
75 return 2 * (w + h);
76 }
77
78 function pointOnRect(t, w, h) {
79 // t goes 0..1 around the rectangle starting from top-left going clockwise
80 const perim = getPerimeter(w, h);
81 const dist = t * perim;
82
83 if (dist <= w) {
84 // top edge: left to right
85 return { x: dist, y: 0 };
86 } else if (dist <= w + h) {
87 // right edge: top to bottom
88 return { x: w, y: dist - w };
89 } else if (dist <= 2 * w + h) {
90 // bottom edge: right to left
91 return { x: w - (dist - w - h), y: h };
92 } else {
93 // left edge: bottom to top
94 return { x: 0, y: h - (dist - 2 * w - h) };
95 }
96 }
97
98 function animate(timestamp) {
99 if (!startTime) startTime = timestamp;
100 const elapsed = (timestamp - startTime) % duration;
101 const t = elapsed / duration;
102
103 const w = canvas.width;
104 const h = canvas.height;
105
106 ctx.clearRect(0, 0, w, h);
107
108 // Draw dim border
109 ctx.strokeStyle = 'rgba(0, 80, 120, 0.4)';
110 ctx.lineWidth = 1;
111 ctx.strokeRect(0.5, 0.5, w - 1, h - 1);
112
113 // Draw shiny spot traveling around the border
114 const trailLength = 0.15; // fraction of perimeter
115 const steps = 80;
116
117 for (let i = 0; i <= steps; i++) {
118 const frac = i / steps;
119 const tPos = (t - frac * trailLength + 1) % 1;
120 const pt = pointOnRect(tPos, w - 1, h - 1);
121
122 // alpha: brightest at head (frac=1), fades at tail (frac=0)
123 const alpha = frac * frac;
124 const brightness = Math.floor(100 + frac * 155);
125
126 ctx.beginPath();
127 ctx.arc(pt.x + 0.5, pt.y + 0.5, 1.5, 0, Math.PI * 2);
128 ctx.fillStyle = `rgba(0, ${brightness}, ${brightness + 50}, ${alpha})`;
129 ctx.fill();
130 }
131
132 // Draw the bright head
133 const headPt = pointOnRect(t, w - 1, h - 1);
134
135 // Glow effect at head
136 const grad = ctx.createRadialGradient(
137 headPt.x + 0.5, headPt.y + 0.5, 0,
138 headPt.x + 0.5, headPt.y + 0.5, 8
139 );
140 grad.addColorStop(0, 'rgba(100, 220, 255, 1)');
141 grad.addColorStop(0.3, 'rgba(0, 180, 220, 0.6)');
142 grad.addColorStop(1, 'rgba(0, 100, 150, 0)');
143
144 ctx.beginPath();
145 ctx.arc(headPt.x + 0.5, headPt.y + 0.5, 8, 0, Math.PI * 2);
146 ctx.fillStyle = grad;
147 ctx.fill();
148
149 requestAnimationFrame(animate);
150 }
151
152 requestAnimationFrame(animate);
153</script>
154</body>
155</html>