You can build a whole Discord server kit from one HTML file: draw every asset as an HTML or SVG board at the exact size Discord wants, then have a Playwright script screenshot each board at 2x and shrink it back down. That's how I made the kit for my server, "Cooking up stuff!": an icon, an animated icon, 10 emojis, 4 stickers, 5 role icons, a banner, an invite background and a welcome header, every one of them still editable text.
Why one HTML file?
A Discord kit sounds small until you count it. Mine is 26 uploads plus a preview, at seven different sizes, from a 64 px role icon to a 1920 x 1080 invite background.
I wanted the server to feel like this site: lime, ink and papaya, Newsreader, Geist and Caveat, stickers and tape. And I wanted to change my mind freely. If the lime looks off next week, I'm not re-exporting 26 files.
So the source is text. Every asset is a <div class="board"> in kit.html, sized in CSS to Discord's exact pixels. Shared drawings, like the chef's hat, live once as an SVG <symbol> and get reused with <use>. A script turns the boards into files.
That's the one idea I'd take from this post: treat brand assets like code. Source you can read and diff, outputs a script rebuilds. The kit came out of a Claude Code session, same as this site.
The mark: a chef's hat that says AJ
The server is called "Cooking up stuff!", so the mark is a chef's toque with my initials on the band, lime on ink, with the papaya swoosh from the site's favicon underneath. No stock art. Every shape is SVG.
Two SVG filters do most of the work. feTurbulence and feDisplacementMap wobble the outlines just enough to look hand-drawn, and the noise has a fixed seed, so the wobble is the same on every run (that matters later). The white die-cut border on the stickers is another filter: feMorphology grows the artwork's own shape by 9 px, floods it white and puts the art back on top. New sticker, free border.
Picking all that myself was the point: skip those decisions and you get the same app as everyone else. A wobbly chef's hat isn't anyone's default.
The animated icon is 40 screenshots
Animation is where HTML pays off. The animated board carries data-frames="40", and the page has one function that poses it for any frame:
// kit.html: render.cjs calls this once per frame
window.setFrame = function (id, i, n) {
const t = i / n, TAU = Math.PI * 2;
if (id === 'icon-anim') {
const bob = Math.sin(t * TAU);
const hop = Math.max(0, Math.sin(t * TAU * 2)); // two little hops per loop
document.getElementById('an-hat').style.transform = `translateY(${-14 * hop}px) rotate(${4 * bob}deg)`;
document.getElementById('an-shadow').style.transform = `scaleX(${1 - 0.18 * hop})`;
// ...plus three sparkles that twinkle a third of a loop apart
}
};The script calls it 40 times and screenshots each pose. Everything runs off t, which stops one step short of 1. The hops are sines, and the sparkles spin exactly a quarter turn, which a four-pointed star hides. So frame 39 flows straight back into frame 0. No jump.
Pillow builds one 255-colour palette from the first frame and maps all 40 frames onto it with no dithering, so the flat lime stays flat instead of crawling with dither noise.
One thing surprised me. kit.html asks for 55 ms a frame, but the GIF says 50. GIF stores delays in hundredths of a second, and Pillow turns 5.5 into 5. So the loop is 2.0 seconds, not 2.2. Nobody will notice. I still enjoyed finding it.
Emojis, stickers and role icons
The 10 emojis are the server's in-jokes: :letHimCook:, :worksOnMyMachine:, :rls: for whenever someone forgets row-level security, and an :lgtm: stamp. They're 128 px, transparent, and 9 to 28 KB each.
Stickers are bigger, with words on. Role icons are tiny: a kitchen brigade from Head Chef down to Fresh Ingredient for new members, each a 64 px coloured circle.
And a welcome header for the top of #welcome, with my illustrated portrait wearing the hat.
Where each file goes, and what Discord allows
I checked every limit below on Discord's help centre on 4 October 2026. Where Discord publishes no number, the table says so.
| File | Size | Where it goes in Discord | Discord's rule | Boosts needed |
|---|---|---|---|---|
icon.png (and 2 alternatives) | 512 x 512, 153 KB | Server Settings, Server Profile, Change Server Icon | At least 128 x 128, in multiples of 128. No file size limit listed | None |
icon-animated.gif | 512 x 512, 40 frames, 605 KB | The server icon | A level 1 perk. No file size limit listed | Level 1 (2 boosts) |
banner.png | 960 x 540, 342 KB | Server Settings, Overview, Server Banner Background | At least 960 x 540, 16:9. Keep the top 48 px simple | Level 2 (7 boosts) |
splash.png | 1920 x 1080, 808 KB | Server Settings, Overview, Server Invite Background | Must be 1920 x 1080, JPG or PNG, never a GIF | Level 1 (2 boosts) |
welcome.png | 1600 x 600, 469 KB | Posted at the top of #welcome | A normal upload: 20 MB on a free account | None |
10 emojis (e- files) | 128 x 128, 9 to 28 KB | Server Settings, Emoji | Under 256 KB. JPEG, PNG, GIF or WEBP | None |
4 stickers (s- files) | 320 x 320, about 50 to 58 KB | Server Settings, Stickers | Exactly 320 x 320, PNG or APNG, up to 512 KB | None |
5 role icons (r- files) | 64 x 64, 5 to 6 KB | Server Settings, Roles, a role, Display, Role Icon | 64 x 64, up to 256 KB, no animation | Level 2 (7 boosts) |
Sources: Discord's boosting FAQ (opens in a new tab), emojis (opens in a new tab), stickers (opens in a new tab), sticker specs (opens in a new tab), role icons (opens in a new tab), banners (opens in a new tab), invite background (opens in a new tab), icon size (opens in a new tab), server profile (opens in a new tab) and attachments (opens in a new tab).
The 10 emojis fit easily in the 50 slots every server gets, and the 4 stickers fit in the 5 free sticker slots. So a server with no boosts can take the icon, emojis, stickers and welcome header today.
The one gap: Discord's help centre gives no file size limit for server icons, banners or invite backgrounds. I looked. So I can't promise a number there.
The render pipeline
render.cjs is 73 lines. Here's everything it does:
Draw a board
One
<div class="board">per asset inkit.html, sized in CSS to Discord's exact pixels. Add theclearclass if it needs a transparent background.Open it at 2x
Playwright loads
kit.htmlin Chromium withdeviceScaleFactor: 2, then waits for the fonts and images.Screenshot each board
el.screenshot()captures just that board. Onclearboards,omitBackground: truekeeps it transparent.Shrink it to size
Pillow resizes the 2x capture with LANCZOS, so a 512 px board becomes a sharp 512 px PNG.
Pose the animation
For a board with
data-frames,window.setFrame(id, i, n)poses it for each frame and every pose gets screenshotted.Make the GIF
One 255-colour palette from the first frame, no dithering, looped forever, saved as
icon-animated.gif.
On the real kit, passing board ids to render only some:
node docs/brand/discord/render.cjs icon bannerpng out/icon.png 512x512png out/banner.png 960x540node docs/brand/discord/render.cjs icon-animgif out/icon-animated.gif 40 framesWhy draw at 2x and shrink? The script's own comment says it: drawn at 2x, then downsampled, for clean edges. Text and thin strokes get four times the pixels, then get averaged down to Discord's exact size.
Does it really re-render in seconds? For one board, yes. Timed in a cloud container on 4 October, one board took 7 to 9 seconds, mostly start-up. The whole kit took 137 seconds, and the GIF alone took 80. So render the board you're working on, and save the full run for the end.
- files from one kit.html
- 27
- emojis, 9 to 28 KB each
- 10
- stickers, about 50 to 58 KB each
- 4
- animated icon, 40 frames
- 605 KB
Then I ran the whole thing from a clean copy and compared every file with the repo. All 27 came back byte-identical (once the preview bug below was dealt with). That's the fixed wobble seed paying off. Same idea as proving a speed change didn't move a pixel: if the output matches, nothing broke.
Three bugs I hit on the way
1. Grey squares behind every emoji
omitBackground sounds like "transparent screenshot, please". Playwright's docs are more precise: it "hides default white background". Anything the page paints itself stays. The page body had a grey background, so every "transparent" emoji came out with a grey square baked in. The fix is one line: body { background: transparent; }.
2. Art hiding under Discord's own UI
Discord draws on top of two of these images: the server name over the top of the banner, and the invite card over the middle of the invite background. My first layouts put things in exactly those spots.
Discord's guides say to keep the banner's top 48 px simple and key parts out from under the invite pop-up. So the banner's words sit low, and the invite background keeps everything around the edges.
3. A preview that was always one run behind
The kit has one more board: a mock-up of everything inside a Discord window, built from <img src="out/icon.png"> and friends. Those images load once, when the page opens, before anything renders. So the preview always shows the previous run's files, and on a clean copy it's a page of broken images. That's my real first run on the left.
The fix is boring: render the preview last, on its own, with node docs/brand/discord/render.cjs preview. A cleaner fix would reload the page before the preview board. I haven't done that yet.
Both are a mock-up I drew in HTML, not a screenshot of Discord, and the member names in them are made up.
Would I do it this way again?
Yes. Every time. The kit lives next to my site's code, rebuilds the same files every run, and a change is one edit and one command, not an afternoon of exporting.
If you run a server, steal the idea: one file, one board per asset, sizes from the table above. And if you want to see the hat in the wild, come and say hi in Cooking up stuff! (opens in a new tab). It's on my community page with everywhere else I hang out.
FAQ
What size should a Discord server icon be?
Discord's minimum is 128 x 128, and it suggests cropping in multiples of 128, so 256, 384 or 512 all work. Mine is 512 x 512. An animated icon needs the server at Boost level 1, which takes 2 boosts.
What are Discord's limits for custom emojis and stickers?
Emojis must be under 256 KB, in JPEG, PNG, GIF or WEBP, with names of at least 2 characters. Stickers must be exactly 320 x 320, PNG or APNG, and up to 512 KB. Every server gets 50 emoji slots and 5 sticker slots without boosts.
Which Discord server customisations need Server Boosts?
Level 1 (2 boosts) adds an animated server icon and a server invite background. Level 2 (7 boosts) adds a static server banner and custom role icons. Level 3 (14 boosts) adds an animated banner.




