Arcs

아크

The observation that almost all living motion traces a slight curve, not a straight line — because it pivots around a joint or bends under gravity.

Also known as: Arc motionMotion arcs
···
html
<div class="lane-label l1">Linear</div>
<div class="lane-label l2">Arc</div>
<canvas id="ac"></canvas>
css
.lane-label{position:absolute;left:6%;font:700 clamp(10px,2.8vmin,13px)/1 ui-monospace,monospace;color:var(--muted);z-index:2}
.l1{top:10%}
.l2{top:50%}
#ac{position:absolute;inset:0;width:100%;height:100%;display:block}
js
const canvas = document.getElementById('ac');
const ctx = canvas.getContext('2d');
const cs = getComputedStyle(document.documentElement);
function col(name, fb) { const v = cs.getPropertyValue(name).trim(); return v || fb; }
const LINE = col('--line', '#e2e0d8');
const ACCENT = col('--accent', '#5b5bf7');
const ACCENT2 = col('--accent-2', '#f25c8a');
let w = 0, h = 0;
function resize() {
  const dpr = Math.min(devicePixelRatio || 1, 2);
  w = innerWidth; h = innerHeight;
  canvas.width = w * dpr; canvas.height = h * dpr;
  ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
}
addEventListener('resize', resize);
resize();

function ball(x, y, r, color, alpha) {
  ctx.globalAlpha = alpha === undefined ? 1 : alpha;
  ctx.beginPath(); ctx.arc(x, y, Math.max(r, 1), 0, Math.PI * 2);
  ctx.fillStyle = color; ctx.fill(); ctx.globalAlpha = 1;
}

const DUR = 2400;
const startT = performance.now();
function loop(now) {
  const t = ((now - startT) % DUR) / DUR;
  ctx.clearRect(0, 0, w, h);
  const x0 = w * 0.12, x1 = w * 0.88;
  const y1 = h * 0.32, y2 = h * 0.78, apex = h * 0.36;
  const r = h * 0.055;

  for (let k = 6; k >= 1; k--) {
    const tt = ((t - k * 0.03) + 1) % 1;
    ball(x0 + (x1 - x0) * tt, y1, r * 0.75, ACCENT, 0.07 * (7 - k));
  }
  ball(x0 + (x1 - x0) * t, y1, r, ACCENT);

  ctx.strokeStyle = LINE; ctx.lineWidth = 1;
  ctx.setLineDash([2, 4]);
  ctx.beginPath(); ctx.moveTo(x0, y2); ctx.lineTo(x1, y2); ctx.stroke();
  ctx.setLineDash([]);
  ctx.beginPath();
  for (let i = 0; i <= 30; i++) {
    const tt = i / 30;
    const px = x0 + (x1 - x0) * tt;
    const py = y2 - Math.sin(tt * Math.PI) * (y2 - apex);
    if (i === 0) ctx.moveTo(px, py); else ctx.lineTo(px, py);
  }
  ctx.stroke();
  for (let k = 6; k >= 1; k--) {
    const tt = ((t - k * 0.03) + 1) % 1;
    const px = x0 + (x1 - x0) * tt;
    const py = y2 - Math.sin(tt * Math.PI) * (y2 - apex);
    ball(px, py, r * 0.75, ACCENT2, 0.07 * (7 - k));
  }
  const px = x0 + (x1 - x0) * t;
  const py = y2 - Math.sin(t * Math.PI) * (y2 - apex);
  ball(px, py, r, ACCENT2);

  requestAnimationFrame(loop);
}
requestAnimationFrame(loop);

Swing your arm and it traces an arc around your shoulder joint; throw a ball and gravity bends its path into a parabola. Animators plot that curve first, then place the in-between drawings along it. A drawing that strays off the arc reads as a glitch — a single frame that looks like it "jumped" for no reason.

Perfectly straight motion actually looks unnatural, reserved for genuinely mechanical things — a robot arm, a laser. Anything organic or alive almost always moves along an arc instead.

In UI this shows up as a modal's entrance path, a tooltip trailing the cursor, or a released card bouncing back along a curve instead of a straight line (via a motion-path, or a two-stage transform) — it reads as noticeably more alive.

The top lane below moves in a straight line, taking the shortest route to its destination. The bottom lane connects the same two points with an arc. Both leave a faint trail behind them so the shape of the path stays visible.

When to use

Use an arc for organic movement — characters, icons, drag releases. For data visualization where exactly where a value lands matters, a straight line reads more precisely.