Balloons
helloordinary speech
stop!jagged shout
stop!!spiky burst
(maybe not)thought cloud
~come here~small whisper
*waves*caption box, no tail
/me wavessame, IRC style
(hmm)parentheses, a thought
Commands
/helpthis page
/roll 2d6roll dice, result in a caption
/fliptoss a coin
/fx BOOMburst it — and teach the word to this comic
/themeroll everyone a new pattern
/title Our Comicname the comic and the browser tab
/recapthe story so far, as a caption
/nick Namechange your pen name
/shrug¯\_(ツ)_/¯
/exportopen the save sheet
Faces
ha lol lmaolaughing
:) thankshappy
:( sorry alonesad
hate mad ughangry
help yikesscared
meh whateverbored
maybe hmmcoy
Poses
hi hey byewaves
I me mypoints at themselves
you yourpoints at the other
What is Comic Chatter?
It turns what you type into a comic strip. You write a line, it picks the camera angle, the character’s expression and gesture, and the shape of the speech balloon — the way Microsoft Comic Chat did in 1996.
How do I make a shout, a thought, or a caption?
Punctuation decides. One exclamation mark is a jagged shout and two is a spiky burst. Parentheses make a thought cloud, tildes make a small whisper, and asterisks or /me make a caption box with no tail.
How do I change a panel after it is drawn?
Click it. You can rewrite the line, lock the expression, and give that one panel its own backdrop. To move a panel, drag it along the strip.
Why do two lines sometimes share a panel?
Because that is how a conversation reads. A reply from the other character joins the panel your last line is in. The same character speaking twice always starts a new panel.
Why does every panel look different?
The camera pushes in by a different amount each time and each character takes a fresh pose. The framing is worked out from how wide the characters are and where their heads sit, so it never crops a face.
Is my comic saved anywhere?
Solo comics never leave your device — no account, no upload. A strip in progress is held for seven days so a reload does not lose it, and Save this comic in Settings keeps one on this device for good, where you can reopen, export or delete it. Shared comics are different: they travel through an encrypted room service, but every name, message, and picture is end-to-end encrypted in your browser first, so the service cannot read your comic.
What does a shared room leave on my computer?
When you join a shared comic, this browser keeps that room’s key so you can rejoin as yourself in one tap instead of losing your character. It is kept for seven days and then deleted on its own. On a computer other people use, you can clear them straight away.
Why is it black and white?
Because Comic Chat was. Pure ink on paper prints cleanly and reads well in a screenshot.
Why is everything in capitals?
Comic lettering is capitals, and the original capitalised every balloon. Switch Lettering to Mixed in Settings to keep your own casing.
Who drew the characters?
Jim Woodring, for Microsoft Comic Chat. The art here is converted from the source Microsoft released under an MIT license in July 2026.
Is Comic Chatter free?
Yes, all of it. No account, no watermark, no limit on how many comics you make, and no feature held back for a paid tier. If you want to throw something in the tip jar there is a link in Credits, and that is the whole business model.
How is this different from other comic makers?
Most comic makers hand you a template and a library of clip art, and you place everything yourself. This one reads your sentence and makes the drawing decisions for you: who is in the panel, how close the camera sits, which expression fits your words, and whether the balloon is speech, a shout, a thought or a caption. You write dialogue, not layout. The trade is control: you get one house style, black and white, drawn by Jim Woodring in 1996.
Can I use the comics I make?
Yes. Export a strip and do what you like with it.
The short, human version of how this works and what it does with your words. It is a summary written to be read, not a contract.
Working on your own
Nothing you type is sent anywhere. There is no account, no upload, and no copy of your comic on any server. Your settings and a draft of the strip in progress are kept in this browser so a reload does not lose your work; the draft clears itself after seven days, and Start over clears it immediately.
Working with other people
A shared comic travels through a relay service so everyone’s browser stays in step. Every message, name and picture is encrypted in your browser before it leaves, with a key that lives only in the invitation link, so the relay carries your comic without being able to read it.
Shared rooms are not moderated. Nobody screens what people write or show, and anyone in a room can copy, screenshot or export the whole comic, including your name and every line you type. Invite people you know. Treat anything you write as something that can leave the room with them. If a room turns into something you did not sign up for, the person who created it can lock or end it, and you can close the tab and keep your own copy.
You are responsible for what you write and for anything you share afterwards. Do not use a shared room for anything unlawful, for harassment, or for other people’s private information.
What this device keeps
Your settings, a draft of the current strip, and the key to any shared room you joined so you can rejoin as yourself. Room keys are deleted after seven days, and the Questions tab has a button that clears them all right now. Nothing here is sent anywhere.
What the internet sees
Ordinary visits are counted with Google Analytics. Opening a room invitation skips analytics entirely, so a shared session is never measured. The comic lettering is served by Google Fonts on every visit, which means Google receives the request for the font file the same way any website using it does.
The comics you make
Yours. Export them, print them, post them. The characters are Jim Woodring’s art for Microsoft Comic Chat, used from the source Microsoft released under an MIT license, and the drawing engine here is a port of that work.
No promises
This is a free thing made by one person. It is offered as-is, with no warranty, and the relay behind shared rooms may be unavailable or go away. Keep anything you care about by exporting it. Nobody here is liable for what you or anyone else does with a comic.
Questions, or something that needs a person: [email protected]
Provenance
Microsoft Comic Chat shipped with Internet Explorer 3.0 in 1996 and the C++ was open-sourced
in 2025 (microsoft/comic-chat, v2.5-beta-1). This is not that code compiled
for the web — there is no emulation layer, no WASM, no transpiler. It is a clean-room-adjacent
re-implementation in JavaScript: the original routines were read, and their decision rules and
numeric constants reproduced by hand. Call it a translation of behaviour rather than of code.
Where a rule here differs from the original, the source comment says so and why.
Which routines were reproduced
panel.cppGetBalloonRect confines balloons to the upper region of the
panel. GetCloudEstimate anchors a balloon’s x near its speaker’s
m_arrowX, except PE_BOX narration, which pins to the free rect’s
left edge. GetInterveningBBox pushes a balloon down only when it horizontally overlaps one
already placed — the level test is asymmetric, so only a balloon entirely to the left permits
level tops. That single rule is why narrow balloons from speakers stood apart sit shoulder to
shoulder instead of stacking.
OrderAvatarsGreedy insertion: each figure is tried at every position and both
facings, scored, and dropped where it rates best. EvalPair costs 4×(distance−1)
for partners standing apart, 40 for facing away from whoever you are talking to, and 4/2 for
world-facing. ComputeDisplacementPenalty adds 1 per changed neighbour, which is the
hysteresis that keeps people from teleporting between panels.
AddLineThe panel-break test: no panel, panel at
MAX_ELEMENTS_PER_PANEL, a real time gap, or a repeat speaker — one avatar cannot
talk twice in one frame. Captions and sound effects are exempt on both sides of that check,
because narration is not an avatar speaking.
balloon.cppBalloon classes as shapes: rounded speech, scalloped thought with
its puff trail, spiked shout, dashed whisper, and CBWoodringBox narration
(PE_BALLOON | PE_BOX, left-justified, no tail, and a deliberately inert
QueryRouteRgn so it never reserves a tail corridor).
format.cppLine breaking. Candidate wraps are scored toward comic proportion
with the least ragged right edge, rather than greedy fill.
fonts.cppThe original built the balloon font once at a fixed height and never
resized it; text that would not fit broke to a new panel. That is why the minimum-lettering
setting here refuses to shrink below a floor and splits instead.
Geometry and rendering
Every panel is composed as SVG inside a 3000×3000 unit square and scaled by the browser to
whatever the strip is drawn at. One geometry, one set of units, so screen and print cannot disagree
— the PDF is the same markup rasterised, not a second layout. Character art is vector path
data extracted from the original avatar files, not bitmaps, so it is resolution-independent at any
export size.
Balloon outlines are generated, not drawn: the text block’s bounding box is offset outward
along per-vertex normals, then smoothed. Consecutive normals are checked so the outline cannot
double back on itself, which is the failure mode that produces self-intersecting balloons. Tails are
separate paths on their own layer beneath the balloon fill. Thought puffs are sized from
BUBBLE_HEIGHT with the stroke scaled alongside the radius — a shrunken puff drawn
with a full-width pen inks as a solid dot.
Fitting the lettering
Point size is found by binary search over a scale factor: try a size, compose the whole panel,
ask whether every element fits, narrow. A second pass equalises the result so one speaker’s
lettering is never visibly larger than the person answering them. The lower bound is your
Smallest-lettering setting; below it the line is pushed to the next panel rather than shrunk into
illegibility.
Determinism
Pose choice, camera framing and backdrop placement all run off a seeded PRNG keyed to the
panel’s id, and each chosen pose is pinned to its utterance. Recomposing a panel — because
a webfont finished loading, or a setting changed, or someone edited a different panel — redraws
it identically. In a room this matters more than tidiness: every browser reduces the same event log
with the same seeds, so everyone is looking at the same strip without any of it being transmitted.
Export pipeline
PNG: the strip SVG is serialised to a blob URL, drawn into a canvas at 2×, and read back with
toBlob. SVG: same markup with any WebP backdrop inlined as PNG so the file stands alone.
PDF: each panel is rasterised, base64’d and placed with jsPDF onto a fixed grid whose cell is
the smaller of the width fit and the height fit, so slack lands in the outer margin instead of
leaving half a page empty. All of it happens in the tab — nothing is uploaded to render.
Rooms: keys
Shared rooms are end-to-end encrypted with AES-GCM via WebCrypto. A room carries two
independent keys — one for events (the lines, moods, panel structure) and one for assets
(uploaded backdrops) — so an image and the script are not protected by the same secret. A third
claim key is derived from the events key with HKDF and proves membership without ever
putting the events key on the wire.
Keys are imported non-extractable, so nothing in the page can read them back out, and they travel
in the URL fragment of an invitation. A fragment is never transmitted in an HTTP request: the
relay operator can see that a room exists and how much traffic it carries, and cannot see a single
line of it. Each payload gets its own random IV, and GCM’s auth tag means a tampered message
fails to decrypt rather than decrypting to something else.
Rooms: transport
A Cloudflare Durable Object holds one room. Because a DO is single-threaded and
addressed by id, it gives total ordering for free: it stamps every event with a monotonic sequence
number, and that sequence is the canonical history. Clients hold lastSeq, keep an
outbox of unacknowledged events, and replay it after a reconnect, so a dropped connection produces a
gap that heals rather than a fork. State is ephemeral and the room ends when everyone leaves. There
are no accounts, so there is no credential to steal and nothing to log in to; the invitation link
is the capability.
Threat model, honestly
Anyone holding the link can read the room — that is what a capability means, so treat the
link as the secret. There is no forward secrecy: one room, one key, for its lifetime. The relay
learns metadata (room existence, timing, message sizes) even though it cannot read content. Nothing
here defends against a compromised browser or a malicious extension, since that is inside the
boundary. And solo work is not encrypted at all because it is never transmitted — it lives in
the tab, and closing it without saving loses it.
Shape of the thing
One HTML file. No build step, no bundler, no package manager, no runtime dependency to install.
Fonts and character art are embedded as base64; jsPDF is the only vendored library. View source and
the entire application is in front of you, comments and all.