Carrom

Carrom

Carrom - Free Online Carrom Board Game: Flick, Pocket, Cover the Queen

3.0

Betyg

0

0

import {useEffect, useRef, useState} from 'react'; export const PIECE_ART = { striker: '/img/games/carrom/pieces/pieceStriker.webp', white: '/img/games/carrom/pieces/pieceWhite.webp', black: '/img/games/carrom/pieces/pieceBlack.webp', queen: '/img/games/carrom/pieces/pieceQueen.webp', };

export function Disc({kind, size = 28, className = ''}) { // The real in-game piece sprites, so the widgets look exactly like the game. return ( <img src={PIECE_ART[kind] ?? PIECE_ART.white} alt="" draggable={false} className={'inline-block select-none drop-shadow-[0_2px_1px_rgba(30,15,5,0.4)] ' + className} style={{width: size, height: size}} /> ); }

export function BoardSetupDiagram() { // Same math as the shared rules engine (CarromPoolGame#getStartingBallLayout): an inner ring of // 6 men touching the queen, an outer ring of 12, whites at inner angles 0/120/240 and outer // slots k where k % 4 <= 1, forming the traditional "Y". Rendered on the real board art. const size = 300; const c = size / 2; const d = 30; const inner = Array.from({length: 6}, (, i) => { const a = (i * Math.PI) / 3; return {x: c + d * 1.02 * Math.cos(a), y: c - d * 1.02 * Math.sin(a), white: i % 2 === 0}; }); const outer = Array.from({length: 12}, (, k) => { const a = (k * Math.PI) / 6; return {x: c + 2 * d * 1.02 * Math.cos(a), y: c - 2 * d * 1.02 * Math.sin(a), white: k % 4 <= 1}; }); return (

<div className="relative" style={{width: size, height: size}}> {inner.concat(outer).map((p, i) => ( <span key={i} className="absolute" style={{left: p.x - 11, top: p.y - 11}}> <Disc kind={p.white ? 'white' : 'black'} size={22} /> ))} <span className="absolute" style={{left: c - 11, top: c - 11}}> <span className="absolute" style={{left: c - 14, top: size * 0.8276 - 14}}>

The opening setup: the red queen in the middle, 6 pieces around her, 12 more around those, and your striker on the baseline.

); }

export function QueenExplainer() { const [step, setStep] = useState(0); const steps = [ { label: '1. Pocket the queen', text: 'You may only pocket the red queen after you have pocketed at least one of your own pieces. Sink her and you keep your turn, but she is not yours yet.', board: ['queen'], }, { label: '2a. Cover her', text: 'Pocket one of your own pieces on that same shot or the very next one. That covers the queen: she stays down for good and the pressure is off.', board: ['queen', 'white'], }, { label: '2b. Miss the cover', text: 'If your next shot pockets nothing of yours, the queen comes back to the center circle and your turn ends. Someone has to win her all over again.', board: [], }, ]; const active = steps[step]; return (

Try it
{steps.map((s, i) => ( <button key={i} onClick={() => setStep(i)} className={ 'rounded-xl px-3 py-1.5 text-sm font-bold shadow-[0_3px_0_#7e22ce] ' + (i === step ? 'bg-purple-400 text-[#130b1f]' : 'bg-[#130b1f] text-white/80') } > {s.label} ))}
{active.board.length === 0 ? ( Back on board ) : ( active.board.map((kind, i) => ) )}

{active.text}

); }

export function MiniCarrom() { // A self-contained toy sim, not the real engine: circles with friction, elastic bounces, and // corner pockets are enough to teach flicking and the queen rules. The real game runs the full // tailuge physics server-side. const SIZE = 300; const CENTER = SIZE / 2; const MAN_R = 11; const STRIKER_R = 14; const POCKET_R = 15; // Calibrated to the real board art: the playing surface is the inner 6.4%..93.6% of the image // and the pocket holes are painted 10.2% in from each corner. const SURFACE_MIN = SIZE * 0.064; const SURFACE_MAX = SIZE * 0.936; const POCKET_AT = SIZE * 0.102; const POCKETS = [ [POCKET_AT, POCKET_AT], [SIZE - POCKET_AT, POCKET_AT], [POCKET_AT, SIZE - POCKET_AT], [SIZE - POCKET_AT, SIZE - POCKET_AT], ]; // Centered on the baseline strip painted in the board art (measured at 82.76% down the image). const BASELINE_Y = SIZE * 0.8276; const START_MESSAGE = 'Press the striker, drag back, and let go to flick.'; function makeStartingBalls() { // Mini version of the real setup: queen in the middle, one ring of 6 touching her, // whites and browns alternating. You play white. const ring = Array.from({length: 6}, (_, i) => { const a = (i * Math.PI) / 3; return { id: i + 2, kind: i % 2 === 0 ? 'white' : 'black', x: CENTER + 23 * Math.cos(a), y: CENTER - 23 * Math.sin(a), vx: 0, vy: 0, r: MAN_R, sunk: false, }; }); return [ {id: 0, kind: 'striker', x: CENTER, y: BASELINE_Y, vx: 0, vy: 0, r: STRIKER_R, sunk: false}, {id: 1, kind: 'queen', x: CENTER, y: CENTER, vx: 0, vy: 0, r: MAN_R, sunk: false}, ...ring, ]; } const [balls, setBalls] = useState(makeStartingBalls); const [message, setMessage] = useState(START_MESSAGE); const [pointer, setPointer] = useState(null); const boardRef = useRef(null); const sim = useRef({balls: null, raf: 0, last: 0, dragging: false, moving: false, hasPottedWhite: false, queenPending: false, queenCovered: false}); useEffect(() => () => cancelAnimationFrame(sim.current.raf), []); function findFreeSpot(list, r) { // First free spot at the center, spiraling outward, used when a piece returns to the board. for (let ringIndex = 0; ringIndex < 8; ringIndex++) { for (let slot = 0; slot < Math.max(1, ringIndex * 6); slot++) { const a = (slot * 2 * Math.PI) / Math.max(1, ringIndex * 6); const x = CENTER + ringIndex * (MAN_R * 2 + 2) * Math.cos(a); const y = CENTER + ringIndex * (MAN_R * 2 + 2) * Math.sin(a); if (list.every((b) => b.sunk || (b.x - x) ** 2 + (b.y - y) ** 2 > (b.r + r + 1) ** 2)) { return {x, y}; } } } return {x: CENTER, y: CENTER}; } function returnToBoard(ball, list) { const spot = findFreeSpot(list, ball.r); ball.sunk = false; ball.x = spot.x; ball.y = spot.y; ball.vx = 0; ball.vy = 0; } function respawnStriker(list) { const striker = list[0]; striker.sunk = false; striker.vx = 0; striker.vy = 0; striker.y = BASELINE_Y; for (let offset = 0; offset < 8; offset++) { for (const sign of [1, -1]) { const x = CENTER + sign * offset * 18; if (list.slice(1).every((b) => b.sunk || (b.x - x) ** 2 + (b.y - BASELINE_Y) ** 2 > (b.r + striker.r + 1) ** 2)) { striker.x = x; return; } } } striker.x = CENTER; } function settle(sunkBefore) { const s = sim.current; const list = s.balls; const newlySunk = list.filter((b) => b.sunk && !sunkBefore.has(b.id)); const strikerFoul = newlySunk.some((b) => b.kind === 'striker'); const whites = newlySunk.filter((b) => b.kind === 'white'); const queenSunk = newlySunk.some((b) => b.kind === 'queen'); const browns = newlySunk.filter((b) => b.kind === 'black'); let msg; if (strikerFoul) { for (const w of whites) { returnToBoard(w, list); } if (queenSunk || s.queenPending) { const queen = list[1]; if (queen.sunk) { returnToBoard(queen, list); } s.queenPending = false; } const penalty = list.find((b) => b.kind === 'white' && b.sunk); if (penalty) { returnToBoard(penalty, list); } msg = 'Foul! The striker went in. Your pieces from this shot come back, plus one already-pocketed piece as a penalty.'; } else if (queenSunk && whites.length === 0 && !s.hasPottedWhite) { returnToBoard(list[1], list); msg = 'The queen only counts after you have pocketed one of your own. She goes back to the center.'; } else if (queenSunk && whites.length > 0) { s.queenCovered = true; s.hasPottedWhite = true; msg = 'Queen down and covered in one shot. She stays down for good.'; } else if (s.queenPending && whites.length > 0) { s.queenPending = false; s.queenCovered = true; s.hasPottedWhite = true; msg = 'Covered! The queen stays down for good.'; } else if (s.queenPending) { s.queenPending = false; returnToBoard(list[1], list); msg = 'No cover. The queen returns to the center, and in a real board your turn would end.'; } else if (queenSunk) { s.queenPending = true; msg = 'Queen down! Now cover her: pocket a white piece with your next flick or she comes back.'; } else if (whites.length > 0) { s.hasPottedWhite = true; msg = browns.length > 0 ? 'One of yours dropped, so you keep your turn. The brown one stays down too, a gift to your opponent.' : 'That one is yours, so you keep your turn. Flick again.'; } else if (browns.length > 0) { msg = 'That piece belongs to your opponent. It stays down, and your turn would pass.'; } else { msg = 'Nothing dropped, so the turn would pass. Try again.'; } if (list.every((b) => b.kind !== 'white' || b.sunk) && s.queenCovered) { msg = 'Board cleared, queen covered. That is the win!'; } respawnStriker(list); setMessage(msg); setBalls(list.map((b) => ({...b}))); } function tick(now) { const s = sim.current; const dt = Math.min(32, now - s.last) / 1000; s.last = now; const list = s.balls; // Constant sliding deceleration, like a real disc on a powdered board. Exponential drag // reads as air hockey: fast pieces glide too far and slow ones crawl forever. const speedDrop = 1000 * dt; for (const b of list) { if (b.sunk) { continue; } // Sleep: snap sub-visible speeds to a dead stop, or resting pieces jiggle forever on // micro-velocities. A collision impulse from a moving piece wakes them. const speed = Math.hypot(b.vx, b.vy); if (speed < 8 || speed <= speedDrop) { b.vx = 0; b.vy = 0; continue; } b.x += b.vx * dt; b.y += b.vy * dt; b.vx *= 1 - speedDrop / speed; b.vy *= 1 - speedDrop / speed; for (const [px, py] of POCKETS) { if ((b.x - px) ** 2 + (b.y - py) ** 2 < POCKET_R ** 2) { b.sunk = true; } } if (b.sunk) { continue; } if (b.x < SURFACE_MIN + b.r) { b.x = SURFACE_MIN + b.r; b.vx = -b.vx * 0.72; } if (b.x > SURFACE_MAX - b.r) { b.x = SURFACE_MAX - b.r; b.vx = -b.vx * 0.72; } if (b.y < SURFACE_MIN + b.r) { b.y = SURFACE_MIN + b.r; b.vy = -b.vy * 0.72; } if (b.y > SURFACE_MAX - b.r) { b.y = SURFACE_MAX - b.r; b.vy = -b.vy * 0.72; } } for (let i = 0; i < list.length; i++) { for (let j = i + 1; j < list.length; j++) { const a = list[i]; const b = list[j]; if (a.sunk || b.sunk) { continue; } const dx = b.x - a.x; const dy = b.y - a.y; const dist = Math.hypot(dx, dy) || 0.001; const overlap = a.r + b.r - dist; if (overlap > 0) { const nx = dx / dist; const ny = dy / dist; // Soft positional correction with a small tolerance: full-strength separation every // frame visibly shoves resting neighbors apart step by step. if (overlap > 0.3) { const push = overlap * 0.6; a.x -= (nx * push) / 2; a.y -= (ny * push) / 2; b.x += (nx * push) / 2; b.y += (ny * push) / 2; } const ma = a.kind === 'striker' ? 1.6 : 1; const mb = b.kind === 'striker' ? 1.6 : 1; const rel = (b.vx - a.vx) * nx + (b.vy - a.vy) * ny; if (rel < 0) { // Slow contacts are nearly dead so clusters settle; only real hits stay springy. const e = -rel < 30 ? 0.2 : 0.93; const impulse = (-(1 + e) * rel) / (1 / ma + 1 / mb); a.vx -= (impulse / ma) * nx; a.vy -= (impulse / ma) * ny; b.vx += (impulse / mb) * nx; b.vy += (impulse / mb) * ny; } } } } setBalls(list.map((b) => ({...b}))); if (list.some((b) => !b.sunk && Math.hypot(b.vx, b.vy) > 8)) { s.raf = requestAnimationFrame(tick); } else { s.moving = false; settle(s.settleBaseline); } } function localPoint(e) { const rect = boardRef.current.getBoundingClientRect(); return {x: e.clientX - rect.left, y: e.clientY - rect.top}; } function onPointerDown(e) { const s = sim.current; if (s.moving) { return; } const p = localPoint(e); const striker = balls[0]; if (Math.hypot(p.x - striker.x, p.y - striker.y) < STRIKER_R + 14) { s.dragging = true; e.currentTarget.setPointerCapture(e.pointerId); setPointer(p); } } function onPointerMove(e) { if (sim.current.dragging) { setPointer(localPoint(e)); } } function onPointerUp(e) { const s = sim.current; if (!s.dragging) { return; } s.dragging = false; setPointer(null); const p = localPoint(e); const striker = balls[0]; let vx = (striker.x - p.x) * 7; let vy = (striker.y - p.y) * 7; const speed = Math.hypot(vx, vy); if (speed < 70) { return; } if (speed > 950) { vx *= 950 / speed; vy = 950 / speed; } s.balls = balls.map((b) => ({...b})); s.balls[0].vx = vx; s.balls[0].vy = vy; s.settleBaseline = new Set(s.balls.filter((b) => b.sunk).map((b) => b.id)); s.moving = true; s.last = performance.now(); s.raf = requestAnimationFrame(tick); } function reset() { const s = sim.current; cancelAnimationFrame(s.raf); s.moving = false; s.dragging = false; s.hasPottedWhite = false; s.queenPending = false; s.queenCovered = false; setPointer(null); setBalls(makeStartingBalls()); setMessage(START_MESSAGE); } const striker = balls[0]; const whitesLeft = balls.filter((b) => b.kind === 'white' && !b.sunk).length; const queenState = sim.current.queenCovered ? 'covered' : sim.current.queenPending ? 'needs cover' : balls[1].sunk ? 'pocketed' : 'on the board'; const aim = pointer && {dx: striker.x - pointer.x, dy: striker.y - pointer.y}; return (

Play a flick
<div ref={boardRef} onPointerDown={onPointerDown} onPointerMove={onPointerMove} onPointerUp={onPointerUp} className="relative shrink-0 select-none rounded-lg shadow-[0_6px_16px_rgba(0,0,0,0.45)] [touch-action:none]" style={{width: SIZE, height: SIZE}} > {/ The real in-game board art. Pockets, baselines, and the mandala are painted on it. */} {aim && Math.hypot(aim.dx, aim.dy) > 8 && ( <span className="absolute h-[5px] origin-left rounded bg-gradient-to-r from-amber-300 to-orange-600 shadow-[0_0_6px_rgba(251,146,60,0.7)]" style={{ left: striker.x, top: striker.y, width: Math.min(135, Math.hypot(aim.dx, aim.dy)), transform: rotate(${Math.atan2(aim.dy, aim.dx)}rad), }} /> )} {balls.map((b) => ( <span key={b.id} className="absolute" style={{transform: translate(${b.x - b.r}px, ${b.y - b.r}px)}}> <span className={'block transition-transform duration-200 ' + (b.sunk ? 'scale-0' : 'scale-100')}> <Disc kind={b.kind === 'black' ? 'black' : b.kind} size={b.r * 2} /> ))}

{message}

Your pieces left: {whitesLeft} · Queen: {queenState}

); }

Carrom Game Online

Foony Carrom is a free online carrom game with realistic striker physics, the full queen and cover rules enforced automatically, and private rooms you can share with one link. You flick a striker across a square wooden board, pocket your nine pieces in the corner pockets, and race your opponent to clear the board. It runs in your browser on desktop or phone with nothing to install.

Carrom started in India and is played across South Asia and far beyond, usually on a living room floor with talcum powder and strong opinions. This version keeps the feel of the real board, the slide of the pieces, the rebounds off the frame, and the tension of the last shot, without anyone needing to own 74 centimeters of plywood.

Play Carrom Online with Friends

Click Play with Friends to open a private room and share the invite link. Anyone with the link joins from a modern browser, no account or install needed. A board of carrom is a head-to-head duel between two players, and extra people in the room can hang out, chat, and rotate in between boards.

Prefer solo practice? Add a bot instead. There are four tiers, Easy, Medium, iCheater, and iCheater Pro Max. Easy leaves you room to learn the aim controls, Medium punishes lazy shots, and the two iCheater tiers test a spread of striker positions and angles before every flick. Hosts can also set a per-turn time limit from 5 to 60 seconds, or turn the timer off entirely.

Carrom Board Setup

The board is a square with a pocket in each corner. Nineteen pieces start in the center: the red queen in the exact middle, an inner circle of six pieces, and an outer circle of twelve. The white pieces form a Y shape pointing out from the queen, which is the traditional arrangement. Each player flicks from their own baseline, the red strip near their edge of the board.

Whoever breaks plays white and goes first. You place the striker anywhere on your baseline before every shot, so picking the right spot along that strip is as much a part of aiming as the angle itself.

How to Play Carrom

On your turn, slide the striker along your baseline, aim, set power, and release. Pocket one of your own pieces and you shoot again. Pocket nothing and the turn passes. Pocket one of your opponent's pieces and it stays down, a gift they will thank you for. The first player to pocket all nine of their pieces wins the board.

Here is a small practice board you can flick right now. You play the light pieces, and it calls out what each result would mean in a real game:

Pocketing the striker itself is a foul. Any of your pieces that fell on that shot come back to the center, one of your previously pocketed pieces returns as a penalty, and your turn ends. There is one more rule that trips up new players: you cannot pocket your last piece while the queen is still on the board. She always has to be settled first.

The Carrom Queen and Covering

The queen is worth nothing on her own. You may only pocket her after sinking at least one of your own pieces, and you must then cover her by pocketing another of your own pieces on the same shot or the very next one. Fail the cover and she returns to the center circle.

Timing the queen is the heart of carrom strategy. Take her too early and you risk handing her back. Leave her too late and your opponent may clear their pieces while you babysit the middle of the board.

Carrom Striker Tips and Strategy

  • Move the striker before you aim. The baseline is wide. Sliding the striker two piece-widths left often turns an impossible cut into a straight shot at a corner pocket.
  • Break toward a corner, not straight ahead. An angled break scatters the formation toward two pockets at once and often drops a piece immediately.
  • Use the frame. A piece hugging a cushion is easiest to cut in along the rail. The rebound angles are honest, so a bank off the frame into a corner is a real weapon, and it earns the Off the Frame achievement.
  • Count before you take the queen. Pocket her when you still have two or three easy pieces left, so the cover shot is a formality instead of a prayer.
  • Mind the last-piece rule. If the queen is still up, plan your order so your ninth piece is not the only shot you have left.

Leaderboards, Achievements, and Progression

Every board feeds the Carrom leaderboards across day, week, month, year, and all-time windows. There are 8 Carrom achievements to chase, from First Flick and Board Winner up to Crowned for covering the queen, Hat Trick for three or more pockets in one turn, Steady Hand for winning without a single striker foul, and Untouchable for winning before your opponent pockets anything. Playing also levels your carrom rank and earns currency drops you can spend across Foony.

Carrom vs Pool

Carrom and pool share the same core idea, send the round thing into the hole, but the feel is completely different. Carrom uses your finger and a flat striker on a dry square board where pieces slide, while 8-ball pool uses a cue stick and rolling balls on cloth. Carrom shoots from a fixed baseline every turn, pool plays the cue ball from wherever it stops. And where pool has solids and stripes, carrom has the queen, a single shared piece both players fight over. Foony Carrom runs on the same physics engine as our pool and snooker tables, so if you can judge an angle in one, you can judge it in all three. Fans of Carrom Pool style mobile games will feel at home here, with the classic rules kept intact.

Carrom: Frequently Asked Questions

How do you play carrom?
Each player has 9 pieces (white or black) plus the shared red queen in the center of a square board with a pocket in each corner. On your turn you place the striker on your baseline, aim, and flick it. Pocket one of your own pieces and you shoot again. The queen only counts if you pocket one of your own pieces right after it, which is called covering. The first player to pocket all 9 of their pieces wins. Foony Carrom enforces all of these rules automatically.
What is the queen in carrom?
The queen is the single red piece that starts in the center of the board. You may only pocket her after you have pocketed at least one of your own pieces, and you must then cover her by pocketing another of your own pieces on the same or the very next shot. If you fail to cover, the queen returns to the center. On Foony Carrom the queen and cover state are always shown, so you never lose track of whether she still needs covering.
What is the difference between carrom and pool?
Carrom is played on a square wooden board where you flick a flat striker with your finger, and every player shoots from a fixed baseline. Pool uses a cue stick and a rectangular cloth table, and you play the cue ball from wherever it stops. Carrom pieces are flat discs that slide; pool balls roll. Foony Carrom runs on the same physics engine as our pool tables, so aim and power control carry over between the games.
Is carrom online free to play?
Yes. Nothing in Foony Carrom costs money: the full rule set, all four bots, and private rooms are open to everyone. An optional account carries your progress between devices and puts your wins on the leaderboards.
Can I play carrom online with friends?
Yes. Hit "Play with Friends" on Foony Carrom and send the room link to whoever you want to beat. The link opens in any modern browser, and nobody has to register or download anything to sit down at the board. Others in the room can watch and swap in when a board ends.
How many players is carrom?
Two per board on Foony Carrom: you and one opponent, each with nine pieces. The room itself is not capped at two, so a bigger group can gather in one place, spectate, and rotate between boards.
Can I practice carrom against a computer opponent?
Yes. Foony Carrom seats a bot at any of four levels: Easy, Medium, iCheater, and iCheater Pro Max. Start on Easy to get a feel for placing and flicking the striker, then climb. The top two levels search the board for the best flick every turn, so beating them takes real queen timing.
What happens if you pocket the striker in carrom?
Pocketing the striker is a foul. Any of your own pieces you pocketed on that shot come back to the center, one of your previously pocketed pieces returns to the board as a penalty, and your turn ends. On Foony Carrom the penalty piece is placed back automatically.
Which country invented carrom?
Carrom originated in India, and it remains hugely popular across South Asia, the Middle East, and Southeast Asia. The International Carrom Federation standardizes tournament rules today. Foony Carrom follows the standard rule set with the queen, covering, and striker fouls.
Can I play carrom in my browser without downloading anything?
Yes. Foony Carrom needs only a browser tab. Chromium browsers, Firefox, and Safari 14 or newer all work, on desktop or phone, and every flick, rebound, and pocket is simulated right on the page.
Does carrom online have leaderboards and achievements?
Yes. Boards feed the day, week, month, year, and all-time leaderboards on Foony Carrom. There are 8 carrom achievements, from First Flick up to Steady Hand and Untouchable, and your account levels up as you play.
Where is the best place to play carrom online?
Foony Carrom. It enforces the real rules end to end, queen covering, striker fouls, the last-piece rule, on a square board with honest rebound physics. Rooms are private, invites are one link, bots come in four levels, and none of it costs a thing.
8 Ball Pool online multiplayer billiards icon