ControlNet

컨트롤넷

Feeding a structural reference image — edges, a pose — alongside the prompt to lock the composition of the result.

Also known as: Structural conditioningPose/edge conditioning
···
html
<div class="wrap">
  <canvas id="cv"></canvas>
  <span class="tag left">컨트롤 맵 (포즈 스켈레톤)</span>
  <span class="tag right" id="rtag">생성 결과: 스타일 A</span>
</div>
css
.wrap{position:relative;width:100%;height:100%}
#cv{position:absolute;inset:0;width:100%;height:100%}
.tag{position:absolute;top:8px;z-index:2;font-size:9.5px;font-weight:700;color:#f1f0ec;
  padding:4px 8px;border-radius:8px;background:rgba(13,13,18,0.55);border:1px solid rgba(255,255,255,0.15)}
.left{left:8px}
.right{right:8px}
js
const cv = document.getElementById('cv'), ctx = cv.getContext('2d');
const rtag = document.getElementById('rtag');
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); }
addEventListener('resize', fit); fit();

const joints = { head: [0.5, 0.18], neck: [0.5, 0.32], lsh: [0.36, 0.35], rsh: [0.64, 0.35], lel: [0.28, 0.5], rel: [0.72, 0.5], lhip: [0.42, 0.58], rhip: [0.58, 0.58], lknee: [0.4, 0.76], rknee: [0.6, 0.76], lft: [0.38, 0.94], rft: [0.62, 0.94] };
const bones = [['head', 'neck'], ['neck', 'lsh'], ['neck', 'rsh'], ['lsh', 'lel'], ['rsh', 'rel'], ['neck', 'lhip'], ['neck', 'rhip'], ['lhip', 'rhip'], ['lhip', 'lknee'], ['rhip', 'rknee'], ['lknee', 'lft'], ['rknee', 'rft']];

function drawSkeleton(offsetX, halfW, h, opts) {
  ctx.lineWidth = opts.lw; ctx.lineCap = 'round'; ctx.strokeStyle = opts.stroke;
  if (opts.glow) { ctx.shadowColor = opts.stroke; ctx.shadowBlur = opts.glow; }
  for (const [a, b] of bones) {
    const ja = joints[a], jb = joints[b];
    ctx.beginPath(); ctx.moveTo(offsetX + ja[0] * halfW, ja[1] * h); ctx.lineTo(offsetX + jb[0] * halfW, jb[1] * h); ctx.stroke();
  }
  ctx.shadowBlur = 0;
  ctx.fillStyle = opts.dotColor;
  for (const k in joints) { const j = joints[k]; ctx.beginPath(); ctx.arc(offsetX + j[0] * halfW, j[1] * h, opts.dot, 0, Math.PI * 2); ctx.fill(); }
}
const themes = [
  { name: '스타일 A', stroke: 'hsl(320 80% 62%)', lw: 7, dot: 5, dotColor: 'hsl(320 90% 78%)', glow: 10 },
  { name: '스타일 B', stroke: 'hsl(160 70% 52%)', lw: 7, dot: 5, dotColor: 'hsl(160 80% 74%)', glow: 10 },
  { name: '스타일 C', stroke: 'hsl(42 90% 55%)', lw: 7, dot: 5, dotColor: 'hsl(42 95% 74%)', glow: 10 },
];
let ti = 0;
function render() {
  const w = innerWidth, h = innerHeight;
  ctx.clearRect(0, 0, w, h);
  drawSkeleton(0, w * 0.5, h, { lw: 2.5, stroke: 'rgba(255,255,255,0.85)', dot: 3.5, dotColor: '#fff' });
  drawSkeleton(w * 0.5, w * 0.5, h, themes[ti]);
  ctx.strokeStyle = 'rgba(255,255,255,0.15)'; ctx.lineWidth = 1;
  ctx.beginPath(); ctx.moveTo(w * 0.5, 0); ctx.lineTo(w * 0.5, h); ctx.stroke();
  rtag.textContent = '생성 결과: ' + themes[ti].name;
}
render();
setInterval(() => { ti = (ti + 1) % themes.length; render(); }, 2400);

A prompt alone can say what to draw but has trouble pinning down exactly where and in what shape. ControlNet feeds structural information extracted from a reference — edges, a pose skeleton, a depth map — through a separate conditioning path, and an extra network trained to preserve that structure keeps pulling generation back toward it throughout.

The difference from image-to-image is that what gets passed is not a full image with color and texture, but abstract structure alone — lines, points. That lets layout stay fixed while style and content are still free for the prompt to decide.

Whatever the structure map does not encode — color, texture, background — is still filled in by the prompt and the model. Forcing the structure too strongly, though, can twist the result awkwardly if that structure does not naturally fit what the prompt is asking for.

The demo below draws the same joint coordinates (a skeleton) as white lines on the left and in shifting colors on the right, standing in for "structure fixed, style free" instead of a real ControlNet.

When to use

Use it when layout, composition, or pose needs to match precisely and the prompt alone keeps missing it. It is an unnecessary constraint for free-form generation where structure does not matter much.