Combo Counter

콤보 카운터

A number that climbs with every successive hit, punching bigger on each step — and resets to zero if the next hit doesn’t land in time.

Also known as: 콤보 게이지Combo meter연속 타격 카운터
···
html
<div class="ccwrap"><div class="ccnum" id="ccnum">0</div><div class="cclabel">COMBO</div><div class="ccbar"><div class="ccfill" id="ccfill"></div></div></div>
css
.ccwrap{display:flex;flex-direction:column;align-items:center;gap:4px}
.ccnum{font:900 clamp(30px,13vmin,56px)/1 monospace;color:var(--fg);transition:transform .12s cubic-bezier(.3,1.6,.4,1),color .2s}
.ccnum.hit{transform:scale(1.25)}
.ccnum.hi{color:var(--accent-2)}
.cclabel{font:700 clamp(8px,2.4vmin,10px) monospace;color:var(--muted);letter-spacing:.15em}
.ccbar{width:min(60%,140px);height:4px;border-radius:2px;background:color-mix(in srgb,var(--fg) 14%,transparent);overflow:hidden;margin-top:2px}
.ccfill{height:100%;width:100%;background:var(--accent);transform-origin:left;transition:transform linear}
js
const num=document.getElementById('ccnum');
const fill=document.getElementById('ccfill');
const WINDOW_MS=850;
let combo=0;
function hit(){
  combo++;
  num.textContent=combo;
  num.classList.remove('hit'); void num.offsetWidth; num.classList.add('hit');
  num.classList.toggle('hi', combo>=8);
  fill.style.transition='none';
  fill.style.transform='scaleX(1)';
  requestAnimationFrame(function(){
    fill.style.transition='transform '+WINDOW_MS+'ms linear';
    fill.style.transform='scaleX(0)';
  });
}
function breakCombo(){
  num.style.transition='transform .3s ease-in, opacity .3s ease-in';
  num.style.transform='scale(0.6) rotate(-8deg)';
  num.style.opacity='0.3';
  setTimeout(function(){
    combo=0;
    num.textContent='0';
    num.classList.remove('hi');
    num.style.transform='scale(1) rotate(0deg)';
    num.style.opacity='1';
    fill.style.transition='none';
    fill.style.transform='scaleX(0)';
  }, 320);
}
const pattern=[260,240,260,230,240,WINDOW_MS+150,260,240,260,230,240,230,220,WINDOW_MS+400];
let pi=0;
function loop(){
  const gap=pattern[pi%pattern.length];
  if(gap>WINDOW_MS) breakCombo(); else hit();
  pi++;
  setTimeout(loop, gap);
}
setTimeout(loop, 500);

A combo counter is two parts: a number, and a timer. The number climbs with each hit and punches with a quick scale-up-then-settle each time, so the count rising itself feels rewarding. The timer — often a radial gauge or a thin bar — makes visible the pressure that the next hit has to land within that window or the combo breaks.

Crossing thresholds (10, 20, 50) often shifts the color or bumps the text size — leaning on the goal-gradient effect (effort intensifies as a goal nears) to reinforce the pull to keep the combo alive.

The moment a combo breaks matters too — the number just vanishing feels anticlimactic, while a small collapse-and-shake as it disappears makes the break register clearly as a failure. Overdo that break animation, though, and it starts to feel punishing, so it's usually kept light.

When to use

Action or rhythm games that reward chained inputs or hits. Unneeded in games with only single, isolated judgments.