Latent space

잠재 공간

An abstract space where images are represented as much smaller numeric vectors instead of pixels. Moving between two points there morphs the image smoothly between them.

Also known as: Latent vector spaceEmbedding space
···
html
<div class="wrap">
  <canvas id="cv"></canvas>
  <div class="plane" id="plane">
    <span class="corner tl">산</span>
    <span class="corner tr">바다</span>
    <span class="corner bl">숲</span>
    <span class="corner br">사막</span>
    <span class="dot" id="dot"></span>
  </div>
</div>
css
.wrap{position:relative;width:100%;height:100%}
#cv{position:absolute;inset:0;width:100%;height:100%}
.plane{position:absolute;right:10px;bottom:10px;width:96px;height:96px;border:1px solid rgba(255,255,255,0.35);
  border-radius:8px;background:rgba(255,255,255,0.06);z-index:2}
.corner{position:absolute;font-size:8.5px;color:rgba(255,255,255,0.8);font-weight:700}
.tl{top:3px;left:4px}
.tr{top:3px;right:4px}
.bl{bottom:3px;left:4px}
.br{bottom:3px;right:4px}
.dot{position:absolute;width:8px;height:8px;border-radius:50%;background:#fff;box-shadow:0 0 8px 2px rgba(255,255,255,0.7);
  transform:translate(-50%,-50%);transition:left 1.6s ease,top 1.6s ease}
js
const cv = document.getElementById('cv'), ctx = cv.getContext('2d');
const dot = document.getElementById('dot');
function fit() { const dpr = Math.min(devicePixelRatio || 1, 2); cv.width = innerWidth * dpr; cv.height = innerHeight * dpr; ctx.setTransform(dpr, 0, 0, dpr, 0, 0); render(currentHue()); }
addEventListener('resize', fit);

const corners = [{ u: 0, v: 0, hue: 20 }, { u: 1, v: 0, hue: 200 }, { u: 0, v: 1, hue: 140 }, { u: 1, v: 1, hue: 35 }];
const waypoints = [[0.08, 0.08], [0.92, 0.08], [0.92, 0.92], [0.08, 0.92]];
let wi = 0, u = 0.08, v = 0.08;
function hueAt(uu, vv) {
  const a = (1 - uu) * (1 - vv), b = uu * (1 - vv), c = (1 - uu) * vv, d = uu * vv;
  return corners[0].hue * a + corners[1].hue * b + corners[2].hue * c + corners[3].hue * d;
}
function currentHue() { return hueAt(u, v); }
function paintScene(w, h, hue) {
  const g = ctx.createLinearGradient(0, 0, 0, h);
  g.addColorStop(0, 'hsl(' + hue + ' 55% 60%)'); g.addColorStop(1, 'hsl(' + ((hue + 40) % 360) + ' 45% 28%)');
  ctx.fillStyle = g; ctx.fillRect(0, 0, w, h);
  ctx.beginPath(); ctx.moveTo(0, h);
  for (let i = 0; i <= 7; i++) { const x = w * i / 7; const y = h * 0.6 + Math.sin(i * 1.4 + hue * 0.05) * h * 0.08; ctx.lineTo(x, y); }
  ctx.lineTo(w, h); ctx.closePath();
  ctx.fillStyle = 'hsl(' + hue + ' 40% 20%)'; ctx.fill();
}
function render(hue) { paintScene(innerWidth, innerHeight, hue); }
function moveTo() {
  [u, v] = waypoints[wi];
  dot.style.left = (u * 100) + '%'; dot.style.top = (v * 100) + '%';
  render(hueAt(u, v));
  wi = (wi + 1) % waypoints.length;
  setTimeout(moveTo, 1900);
}
fit();
moveTo();

An encoder compresses an image into a much smaller grid of numbers — the latent — that a decoder can reconstruct back into pixels. "Latent diffusion" models run the whole noise-to-image process inside that compressed space rather than on pixels, decoding to pixels only once at the very end, which is far cheaper than running the full process at full resolution.

Points that are close together in this space tend to decode into visually related images. So moving in a straight line between two latent vectors (interpolating) produces a smooth in-between image rather than a jarring cut — this is where morphing animations between two generated images come from.

The space is not really a "2D map," though — it has hundreds or thousands of dimensions. Closeness there is also not guaranteed to always track semantic similarity, so interpolation does not always land on a smooth, plausible result.

The demo below flattens the real latent space onto just two axes purely for visualization — four fixed concepts anchor the corners, and a point moving between them drives a weighted blend of color and shape, a procedural simulation, not the actual space.

When to use

Useful for morphing smoothly between two results, or exploring "somewhere between this concept and that one." For precise control, an explicit conditioning method like ControlNet fits better.