Skip to content

Instantly share code, notes, and snippets.

@raphlinus
Created July 4, 2026 03:11
Show Gist options
  • Select an option

  • Save raphlinus/e5cc2ea3cfc2fafa4406cbd708c4fe52 to your computer and use it in GitHub Desktop.

Select an option

Save raphlinus/e5cc2ea3cfc2fafa4406cbd708c4fe52 to your computer and use it in GitHub Desktop.
Squircle test
<html>
<style>
.controls {
display: grid;
grid-template-columns: 180px 200px 100px;
}
.controls input {
width: 180px;
}
.label {
justify-self: end;
}
</style>
<div>
<svg id="svg" width="500" height="400">
<path id="p" d="" fill-rule="evenodd"/>
</svg>
</div>
<div class="controls">
<div class="label">Outer border radius:</div>
<div><input type="range" id="ob" min="0" max="100" step="any"></div>
<div id="obval"></div>
<div class="label">Inner border adjustment:</div>
<div><input type="range" id="ib" min="-1.5" max="1.5" value="0" step="any"></div>
<div id="ibval"></div>
<div class="label">Stroke width:</div>
<div><input type="range" id="w" min="0" max="100" value="5" step="any"></div>
<div id="wval"></div>
<div class="label">Exponent:</div>
<div><input type="range" id="exp" min="1" max="10" value="5" step="any"></div>
<div id="expval"></div>
</div>
</div>
<script src="squircle.js"></script>
</html>
class BezPath {
constructor() {
this.d = ''
}
moveto(x, y) {
this.d += `M ${x} ${y}`;
}
lineto(x, y) {
this.d += `L ${x} ${y}`;
}
curveto(x1, y1, x2, y2, x3, y3) {
this.d += `C ${x1} ${y1} ${x2} ${y2} ${x3} ${y3}`;
}
squircle(x0, y0, x1, y1, r, k) {
const {a, b} = squircleHalfCorner(k);
const c = Math.pow(0.5, 1 / k);
this.moveto(x0 + r, y0);
this.lineto(x1 - r, y0);
this.curveto(x1 - r * (1 - a), y0, x1 - r * (1 - c + b), y0 + r * (1 - c - b), x1 - r * (1 - c), y0 + r * (1 - c));
this.curveto(x1 - r * (1 - c - b), y0 + r * (1 - c + b), x1, y0 + r * (1 - a), x1, y0 + r);
this.lineto(x1, y1 - r);
this.curveto(x1, y1 - r * (1 - a), x1 - r * (1 - c - b), y1 - r * (1 - c + b), x1 - r * (1 - c), y1 - r * (1 - c));
this.curveto(x1 - r * (1 - c + b), y1 - r * (1 - c - b), x1 - r * (1 - a), y1, x1 - r, y1);
this.lineto(x0 + r, y1);
this.curveto(x0 + r * (1 - a), y1, x0 + r * (1 - c + b), y1 - r * (1 - c - b), x0 + r * (1 - c), y1 - r * (1 - c));
this.curveto(x0 + r * (1 - c - b), y1 - r * (1 - c + b), x0, y1 - r * (1 - a), x0, y1 - r);
this.lineto(x0, y0 + r);
this.curveto(x0, y0 + r * (1 - a), x0 + r * (1 - c - b), y0 + r * (1 - c + b), x0 + r * (1 - c), y0 + r * (1 - c));
this.curveto(x0 + r * (1 - c + b), y0 + r * (1 - c - b), x0 + r * (1 - a), y0, x0 + r, y0);
}
}
// Adapted closely from https://developer.chrome.com/blog/implementing-corner-shape
function squircleHalfCorner(k) {
const p0 = 1.2430920942724248
const p1 = 2.010479023614843
const p2 = 0.32922901179443753
const p3 = 0.2823023142212073
const p4 = 1.3473704261055421
const p5 = 2.9149468637949814
const p6 = 0.9106507102917086
const s = Math.log2(k)
const slope = p0 + (p6 - p0) * 0.5 * (1 + Math.tanh(p5 * (s - p1)))
const base = 1 / (1 + Math.exp(slope * p1))
const logistic = 1 / (1 + Math.exp(slope * (p1 - s)))
const a = (logistic - base) / (1 - base)
const b = p2 * Math.exp(-p3 * Math.pow(s, p4))
return {a, b}
}
function render() {
const iba = parseFloat(document.getElementById('ib').value);
const ob = parseFloat(document.getElementById('ob').value);
const w = parseFloat(document.getElementById('w').value);
const k = parseFloat(document.getElementById('exp').value);
document.getElementById('obval').textContent = ob.toFixed(3);
document.getElementById('ibval').textContent = iba.toFixed(3);
document.getElementById('wval').textContent = w.toFixed(3);
document.getElementById('expval').textContent = k.toFixed(3);
const path = document.getElementById('p');
const p = new BezPath();
p.squircle(10, 10, 490, 390, ob, k);
const ib = Math.max(ob + w * (iba - 1), 0);
p.squircle(10 + w, 10 + w, 490 - w, 390 - w, ib, k);
path.setAttribute('d', p.d);
}
function init() {
const ib = document.getElementById("ib");
ib.addEventListener('input', (e) => { render() });
const ob = document.getElementById("ob");
ob.addEventListener('input', (e) => { render() });
const w = document.getElementById("w");
w.addEventListener('input', (e) => { render() });
const exp = document.getElementById("exp");
exp.addEventListener('input', (e) => { render() });
render();
}
init()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment