Back to Huggybugi
English

How to make characters and items

One PNG per action, with the frames laid out side by side.

Use the skill · easiest way

Upload the skill zip to Claude, ChatGPT or Gemini with a photo. It shows you the drawing, fixes it with you, and hands back a zip you can drop straight in.

Get the skill and use it

Copy the spec and ask

Copy this page as markdown, paste it into ChatGPT or Claude, and it will follow the spec.

Uploading

In the tray menu go to "Character > Create…", pick the Characters, Items or Emotes tab, then "+ New". Give it a name (up to 20 characters) and pick a PNG for each action, or drag one onto the slot. The fps fields come pre-filled with suggested values. For items you also pick a kind (throwable, food, poop) and an optional wav sound. Save and it joins your list, where you can edit or delete it. The + card at the front of the character, item and emote pickers opens the same editor.

It reaches your friends when they open the tray "Character > Change…" window or click "Get new characters and items" in the tray. The character you picked shows up right away.

For food, right-click a friend, pick one under "Food", then click the friend to send it flying. Every click feeds them until you pick a throwable again.

For poop, the fly image is drawn as poop sitting on the ground (it is never thrown or eaten). Your character leaves the poop you picked when it gets full. Friends who have not downloaded it yet see the default poop until they do.

Emotes

In the "Emotes" tab pick a GIF, WebP, APNG, PNG or JPG. The app scales it to 64px, keeps up to 16 frames and saves it at the original speed (source up to 20MB and 2048px per side). Names are up to 20 characters and must be unique within the group. : and spaces are not allowed because they would break :name: inside a message, and the built-in emote names (heart·question·exclamation·angry·lol·note) are taken.

Where you use them: 'Emotes' in the menu that opens when you click your character (⌃⌥⌘E, Ctrl+Alt+E on Windows) shows the picker above your head. Type : in a message to see the names, and picking one drops :name: into the bubble. The emoji button on the 'Status' tab of the status window puts one in your status. You can also react to a message with one in the chat. Only friends in the same group see them.

Specs

Character actions

You only need idle, walk and sleep. Anything else falls back to another action. Without offline they turn into a stone when offline.

ActionWhenSuggestedIf missing
idle requiredStanding still2 frames, 2fps
walk requiredWalking4 frames, 6fps
sleep requiredAway for 30 minutes or screen locked (the app draws the Zzz)2 frames, 1fps
pokedPoked2 frames, 8fpsidle
throwThrowing3 frames, 10fpsidle
hitGetting hit2 frames, 8fpspoked
jumpDodging, jump action2 frames, 8fpsidle
eatBeing fed, yum action3 frames, 6fpshappy
waveGreeting a friend who came online, hi action3 frames, 6fpshappy
happyBeing petted, happy action2 frames, 4fpsidle
offlineOffline (knocked out)1 frame, 1fpsStone

Item actions

ActionWhenSuggestedIf missing
fly requiredWhile it flies. Turn on 'Spin while flying' in the editor and the app spins it for you (it is off for anything new). With spin off, a throwable flies straight while facing the way it travels, so draw it facing right. Food with spin off flies exactly as drawn, so draw it upright. Add motion with frames so it does not look frozen (bouncing, sparkling). Poop is drawn sitting on the ground1 frame for throwables, 1 to 4 for food, 12fps (1 frame for poop)
impactThe moment it hits (throwables only, food is eaten on arrival)3 frames, 12fpsThe star burst the app draws

The wav plays the moment it lands (for poop, when it drops). Without one, throwables and food are silent and poop uses the app's own 'poot'.

Example: toy hammer

One of the built-in items. fly is a single frame, but it spins while flying (food does not).

ActionMotionWhat you upload (4x)
fly
While it flies
Toy hammer flyfly 1 frame
impact
The moment it hits
Toy hammer impactimpact 3 frames

Download at real size: fly, impact, Download all (zip)

Poop

A poop item only needs one fly frame. It is drawn sitting on the ground, never rotated. The app draws the 48×48 grid as is without scaling, so keep it small in the bottom center (the default poop is 17 wide and 12 tall) and leave the top empty. It does not have to look like poop. A small prop that suits your character (a mini pumpkin, a coffin, a ball of yarn) works too, as long as it fits the bottom-center 24×24 area. The default poop is shown below.

ActionMotionWhat you upload (4x)
fly
Sitting on the ground
Default poop flyfly 1 frame

Download at real size: fly

Example: Ddeoggi

Every action of Ddeoggi, drawn from a penguin plush. The frame counts match the table above, and since every pose reuses the same body the actions line up with each other.

ActionMotionWhat you upload (4x)
idle
Standing still
Ddeoggi idleidle 2 frames
walk
Walking
Ddeoggi walkwalk 4 frames
sleep
Asleep
Ddeoggi sleepsleep 2 frames
poked
Poked
Ddeoggi pokedpoked 2 frames
throw
Throwing
Ddeoggi throwthrow 3 frames
hit
Getting hit
Ddeoggi hithit 2 frames
jump
Dodging, jumping
Ddeoggi jumpjump 2 frames
eat
Being fed
Ddeoggi eateat 3 frames
wave
Greeting
Ddeoggi wavewave 3 frames
happy
Petting
Ddeoggi happyhappy 2 frames
offline
Offline
Ddeoggi offlineoffline 1 frame

Download at real size: idle, walk, sleep, poked, throw, hit, jump, eat, wave, happy, offline, Download all (zip)

Templates

The "Get template" button in the editor gives you a template with the app's Hamzzi (an acorn for items) drawn into every action. Blank templates are here: 2 frames, 4 frames, 8 frames, Download all (zip)

Put it on a layer underneath, draw on top, then hide the template layer before saving. The red line is where the feet go.

Drawing tools: Piskel(web, free), LibreSprite(free), Aseprite(paid)

Make with AI

Image models cannot hit exactly 48 pixels. Generate big, scale down, then clean up.

  1. Generate with the prompt below
  2. Scale each frame down to 48×48. The resampling must be nearest neighbor, or it goes blurry.
  3. Make the background transparent and line the frames up

In the browser, nothing to install:

The base look

Pixel art sprite of a cute [animal], side view facing right, full body,
match the body shape, proportions and features of the attached picture if any
(otherwise the real proportions of that animal or character),
on a 48x48 pixel grid, upscaled so one art pixel is a clean 16x16 block
(768x768 per frame), hard edges, no anti-aliasing, no gradients, no dithering,
about 8 colors, 1px dark outline, flat shading, transparent background,
fills most of the grid (about 30-46 pixels tall), feet on the bottom row,
horizontally centered, nothing touching the top or side edges, no shadow, no text

Action sheet (attach the result above as a reference)

Pixel art sprite sheet of the same character, [N] frames in one horizontal row,
no gaps, facing right, animation: [motion],
each frame on a 48x48 pixel grid, upscaled so one art pixel is a clean 16x16 block
(768x768 per frame), hard edges, no anti-aliasing, no gradients, no dithering,
same palette (about 8 colors) as the reference, 1px dark outline,
same size and position in every frame, feet on the bottom row,
transparent background, the 4 corner pixels of every frame transparent,
nothing touching the top or side edges, no shadow, no text
Action[N][motion]Ddeoggi
idle2gentle breathing, body down 1px in frame 2Ddeoggi idle
walk4walk cycle: left foot forward, body up, right foot forward, body upDdeoggi walk
sleep2eyes closed, body rises and falls 1pxDdeoggi sleep
poked2squashed flat, then back to normalDdeoggi poked
throw3wind up, then swing one arm forwardDdeoggi throw
hit2knocked back, dizzy swirl eyesDdeoggi hit
jump2crouch, then stretch upDdeoggi jump
eat3mouth open, chewing, cheeks puffedDdeoggi eat
wave3one paw raised, wavingDdeoggi wave
happy2smiling closed eyes, tiny heartDdeoggi happy
offline1knocked out, squashed flat on the ground, X eyesDdeoggi offline

Items

Pixel art game item, [item], one single frame on a 48x48 pixel grid,
upscaled so one art pixel is a clean 16x16 block (768x768), hard edges,
no anti-aliasing, no gradients, no dithering, centered in the frame,
about 8 colors, 1px dark outline, transparent background,
the 4 corner pixels transparent, no shadow, no text

For a throwable, draw a single fly frame like the toy hammer and let the app spin it with 'Spin while flying'. For something with a direction, like an arrow or a claw, turn it off and draw it facing right: Toy hammer fly Toy hammer impact

A throwable needs its own impact (food and poop do not have one):

Pixel art impact effect sprite sheet, 3 frames in one horizontal row, no gaps,
a star bursting from the center of the frame and breaking into pieces,
each frame on a 48x48 pixel grid, upscaled so one art pixel is a clean 16x16 block
(768x768 per frame), hard edges, no anti-aliasing, no gradients, no dithering,
centered in every frame, about 8 colors, 1px dark outline, transparent background,
the 4 corner pixels of every frame transparent, no shadow, no text

With 'Spin while flying' off, food flies upright. Use several frames to give it some motion:

Pixel art food item, [item], [N] frames in one horizontal row, no gaps,
upright in every frame (not spinning), gently bobbing up and down with a small sparkle,
each frame on a 48x48 pixel grid, upscaled so one art pixel is a clean 16x16 block
(768x768 per frame), hard edges, no anti-aliasing, no gradients, no dithering,
centered in every frame, about 8 colors, 1px dark outline, transparent background,
the 4 corner pixels of every frame transparent, no shadow, no text

Poop

[character] is your character's name and look (for example "Ddeoggi, a grey penguin with a yellow beak").

Pixel art of a cute squat swirl poop with a tiny face colored and decorated like
[character] (one feature such as ears or beak), or a small prop that matches
[character] (for example a mini pumpkin or a coffin), one single frame on a 48x48 pixel grid,
upscaled so one art pixel is a clean 16x16 block (768x768), hard edges,
no anti-aliasing, no gradients, no dithering, small (about 17x12 pixels),
horizontally centered, sitting on the bottom row, entirely inside the bottom-center
24x24 area of the grid (x 12-35, y 24-47), about 8 colors, 1px dark outline,
transparent background, no shadow, no text

Use the skill (easiest way)

Download huggybugi-character.zip, upload it to Claude, ChatGPT or Gemini together with a photo and say "follow SKILL.md and make a Huggybugi character from this photo". It draws all 11 actions (plus poop, a throwable or food if you ask) and hands back a zip you can drop into 'Add them all at once' below. The AI has to be able to run code (on Claude and ChatGPT, use a model that can). If uploading it every time gets old, register it as a skill. After that a photo and "make me a Huggybugi character" is enough.

Claude Code and Codex install it as a plugin, no zip needed, and a plugin update brings new versions along. You can also copy these commands and the spec from "Make with AI" in the app editor.

# Claude Code
/plugin marketplace add Zaehyeon2/huggybugi
/plugin install huggybugi@huggybugi

# Codex
codex plugin marketplace add Zaehyeon2/huggybugi
codex plugin add huggybugi@huggybugi

These menu names change from time to time. See README.md inside the zip for details.

AI that can run code (ChatGPT, Claude and friends)

The grid comes out exact. Define the body, eyes and feet as simple shapes like ellipses, then build every action from one function that takes pose values (body height, eye shape, foot lift) and draws a single frame. That way the actions never drift apart. Attach a photo or a drawing and it follows the colors and the look. With no picture, have it look up a few cute pixel art versions of that animal or item first and it comes out much better.

Write and run a Python (Pillow) script that draws a pixel art [animal] (see the attached
picture) for a 48x48 desktop pet. Match the picture's body shape, proportions and
features as closely as the grid allows (no picture: first look up a few cute pixel art references of that animal or item and follow their proportions, without copying any one of them). Build each frame from simple shapes (ellipses for body,
face, feet) in ONE function draw(pose) that takes pose values like bob, eyes, foot lift,
so every animation reuses the same shapes. Add a 1px dark outline automatically around
the filled pixels. Facing right, feet touching row 47, the 4 corner pixels transparent.
Save one horizontal strip PNG per animation (1x, frames 48x48, no gaps), each file named
exactly after the animation (idle.png, walk.png, ...):
idle 2, walk 4, sleep 2, poked 2, throw 3, hit 2, jump 2, eat 3, wave 3, happy 2, offline 1.
Put all the PNGs in one zip file for download.
Show me an 8x preview, then let me ask for changes.

Items and poop have different actions, so use this instead of the prompt above. Keep only the line for your kind, throwable or food, and delete the other.

Write and run a Python (Pillow) script that draws a pixel art [item] (see the attached
picture) for a 48x48 desktop pet app. Build it from simple shapes with a 1px dark outline,
transparent background, centered in the frame, the 4 corner pixels of every frame
transparent. Save horizontal strip PNGs (1x, frames 48x48, no gaps), keeping only the
line for your item kind:
thrown item: fly 1 facing right (with 'spin while flying' on, the app spins it, so do not draw spin frames)
and impact 3 (the moment it hits).
food: fly 1-4 drawn upright (it is not spun), gently bobbing with a small sparkle,
no impact (food is eaten on arrival). Name the files exactly fly.png and impact.png and
put them in one zip file. Show me an 8x preview, then let me ask for changes.
Write and run a Python (Pillow) script that draws a pixel art poop for a 48x48 desktop pet,
colored and decorated like [character] (one feature such as ears or beak), or a small prop
that matches [character] instead of a poop. One 48x48 frame
saved as fly.png: small (about 17x12 pixels), horizontally centered, sitting on the bottom
row, entirely inside the bottom-center 24x24 area (x 12-35, y 24-47), flat and cute,
1px dark outline, transparent background. Show me an 8x preview.

Item code looks roughly like this (the toy hammer fly and impact, plus the default poop). The hammer and poop images in the examples above came out of this code. File names are just the action names, fly.png and impact.png, but the example below draws two items at once so it prefixes the names.

import math

COLORS = {'K': '#2b2233', 'Y': '#ffd23f', 'R': '#ff5a6e', 'F': '#9a6a48', 'N': '#7a4a24',
          'E': '#1b1420', 'P': '#ff9fb0'}
N = 48    # 48x48 frames

def oval(x, y, cx, cy, rx, ry):  # true if the pixel center (+0.5) is inside the ellipse
    return ((x + 0.5 - cx) / rx) ** 2 + ((y + 0.5 - cy) / ry) ** 2 <= 1

# build one frame from a color letter per pixel, then turn the outer edge into a 1px dark line (K).
def render(fill):
    g = [[fill(x, y) for x in range(N)] for y in range(N)]
    solid = lambda x, y: 0 <= x < N and 0 <= y < N and g[y][x] != '.'
    return [''.join('K' if c != '.' and not (solid(x - 1, y) and solid(x + 1, y)
                                             and solid(x, y - 1) and solid(x, y + 1)) else c
                    for x, c in enumerate(row)) for y, row in enumerate(g)]

# toy hammer: one fly frame (with 'spin while flying' on, the app spins it)
def hammer(x, y):
    if 12 <= x <= 35 and 10 <= y <= 21:
        return 'Y' if x <= 14 or x >= 33 else 'R'
    if 21 <= x <= 26 and 22 <= y <= 41:
        return 'Y'
    return '.'

# impact: a star grows, then breaks into pieces (3 frames)
def star(r):
    def fill(x, y):
        dx, dy = x + 0.5 - 24, y + 0.5 - 24
        a = math.atan2(dy, dx) + math.pi / 2
        edge = r * (0.55 + 0.45 * abs(math.cos(a * 5 / 2)))
        return 'Y' if math.hypot(dx, dy) <= edge else '.'
    return fill

def burst(color, k, n=6):
    def fill(x, y):
        r, s = 10 + k * 5, 4.4 - k * 0.45
        for i in range(n):
            a = i / n * math.pi * 2 + k * 0.2
            if oval(x, y, 24 + math.cos(a) * r, 24 + math.sin(a) * r, s, s):
                return color
        return '.'
    return fill

# poop: one fly frame. three coils touching the bottom row, with eyes, cheeks and mouth on the lowest one
TIERS = [(23.5, 38.8, 3.4, 1.9), (23.5, 41.9, 5.8, 2.2), (23.5, 44.9, 8.1, 3.1)]
DOTS = {(22, 36): 'F', (23, 36): 'F', (24, 36): 'F', (20, 44): 'E', (26, 44): 'E',
        (18, 45): 'P', (19, 45): 'P', (27, 45): 'P', (28, 45): 'P',
        (22, 46): 'N', (23, 46): 'N', (24, 46): 'N'}

def poop(x, y):
    if (x, y) in DOTS:
        return DOTS[(x, y)]
    for i, (cx, cy, rx, ry) in enumerate(TIERS):
        if oval(x, y, cx, cy, rx, ry):
            # the bottom of each coil is the groove where it rests on the one below
            return 'N' if i < len(TIERS) - 1 and y >= cy + ry - 1.1 else 'F'
    return '.'

ANIMS = {
    'hammer-fly': [render(hammer)],
    'hammer-impact': [render(star(9)), render(star(15)), render(burst('Y', 2))],
    'poop-fly': [render(poop)],
}

# one horizontal strip PNG per action (1x, 48x48 frames)
def save(name, frames):
    from PIL import Image, ImageColor
    img = Image.new('RGBA', (N * len(frames), N))
    for i, g in enumerate(frames):
        for y, row in enumerate(g):
            for x, c in enumerate(row):
                if c != '.':
                    img.putpixel((i * N + x, y), ImageColor.getrgb(COLORS[c]))
    img.save(name + '.png')

if __name__ == '__main__':
    for name, frames in ANIMS.items():
        save(name, frames)

The code an AI writes looks roughly like this (Ddeoggi, all 11 actions). The Ddeoggi images in the examples above came out of this code. With Pillow installed you can run it and get one PNG per action.

import math

COLORS = {'K': '#3a3640', 'G': '#9a98a3', 'g': '#7f7d88', 'W': '#fbf9f4', 'Y': '#f7c535',
          'y': '#e2a51f', 'E': '#2a1c18', 'P': '#ffb3c0', 'R': '#e8586a'}
N = 48    # 48x48 frames
S = 1.5   # scale the measurements chosen for 32 up to 48

def rnd(v):  # round .5 up (python round goes to even, so it is not used)
    return math.floor(v + .5)

def u(v):  # pose offsets as whole pixels on the 48 grid
    return int(math.copysign(rnd(abs(v) * S), v)) if v else 0

def oval(x, y, cx, cy, rx, ry):
    return ((x + .5 - cx) / rx) ** 2 + ((y + .5 - cy) / ry) ** 2 <= 1

def tri(x, y, a, b, c):
    px, py = x + .5, y + .5
    s = lambda p, q: (px - q[0]) * (p[1] - q[1]) - (p[0] - q[0]) * (py - q[1])
    d = (s(a, b), s(b, c), s(c, a))
    return not (min(d) < 0 and max(d) > 0)

# one eye (3x3 base). dx, dy are offsets from the top-left of the eye
def eye(dx, dy, kind):
    if kind == 'open':
        return ('W' if dx == dy == 0 else 'E') if 0 <= dx <= 2 and 0 <= dy <= 2 else None
    if kind == 'wide':  # startled: 4x4 with a highlight
        return ('W' if dx == dy == -1 else 'E') if -1 <= dx <= 2 and -1 <= dy <= 2 else None
    hit = {'closed': (dy == 1 and dx in (-1, 3)) or (dy == 2 and 0 <= dx <= 2),
           'happy': (dy == 0 and 0 <= dx <= 2) or (dy == 1 and dx in (-1, 3)),
           'hurt': -1 <= dx <= 3 and -1 <= dy <= 3 and (dx == dy or dx == 2 - dy)}[kind]
    return 'E' if hit else None

# draw one frame from pose values alone. every action reuses the same shapes.
# squash: + flatter, - taller. feet: foot lift. flip: wing lift. lean: body shifted sideways
def draw(bob=0, squash=0, feet=(0, 0), flip=(0, 0), eyes='open', mouth='', blush=False, lean=0):
    ry = (9 - squash) * S
    rx = min((13 + squash * .8) * S, 22.4)  # keep a squashed body inside the grid
    by = (30.4 + min(bob, 1)) * S - ry + max(bob - 1, 0) * S
    cx = min(max((16 + lean) * S, rx + 1.4), 46.6 - rx)
    wl, wr = max(cx - rx + .3 * S, 4.5), min(cx + rx - .3 * S, 43.5)
    fy = by + 1.5 * S
    ey = rnd(by - 3 * S)
    shift = 3 + u(lean)  # the face sits 3 pixels to the right
    gy = rnd((30 + min(bob, 0)) * S) - u(max(feet))

    def foot(fcx, x, y, up):
        b = 47 - u(up)
        return b - 2 + min(0, u(bob + up)) <= y <= b and abs(x + .5 - fcx) <= (3.5 if y == b else 4.8)

    def pixel(x, y):
        body = oval(x, y, cx, by, rx, ry) or tri(
            x, y, (cx - 3.5 * S, by - ry + 2 * S), (cx + 3.5 * S, by - ry + 2 * S), (cx, by - ry - .8 * S))
        fin = oval(x, y, wl, fy - flip[0] * S, 2.6 * S, 1.8 * S) or oval(x, y, wr, fy - flip[1] * S, 2.6 * S, 1.8 * S)
        paw = foot(18.5, x, y, feet[0]) or foot(31.5, x, y, feet[1])
        # paint the feet before the body so they sit in front of it, like a plush
        if paw and y >= gy:
            return 'Y'
        if not body:
            return 'g' if fin else 'Y' if paw else '.'
        fx = x - shift
        e = eye(fx - 14, y - ey, eyes) or eye(fx - 31, y - ey, eyes)
        if e:
            return e
        if y in (ey + 2, ey + 3) and 21 <= fx <= 26:  # beak
            return 'Y'
        if mouth == 'open':  # open while eating, so the mouth shows
            if ey + 4 <= y <= ey + 5 and 22 <= fx <= 25:
                return 'R'
            if y == ey + 6 and 22 <= fx <= 25:
                return 'Y'
        elif y == ey + 4 and (21 if mouth == 'chew' else 22) <= fx <= (26 if mouth == 'chew' else 25):
            return 'y'
        if blush and y in (ey + 3, ey + 4) and (11 <= fx <= 13 or 34 <= fx <= 36):
            return 'P'
        face = (oval(fx, y, 11 * S + .5, by - 2 * S, 3.8 * S, 3.6 * S)
                or oval(fx, y, 21 * S + .5, by - 2 * S, 3.8 * S, 3.6 * S)
                or oval(fx, y, 16 * S + .5, by + 3 * S, 8.2 * S, 5.2 * S))
        return 'W' if face else 'G'

    g = [[pixel(x, y) for x in range(N)] for y in range(N)]
    solid = lambda x, y: 0 <= x < N and 0 <= y < N and g[y][x] != '.'
    edge = lambda x, y: not all(solid(x + a, y + b) for a, b in ((1, 0), (-1, 0), (0, 1), (0, -1)))
    return [''.join('K' if c != '.' and edge(x, y) else c for x, c in enumerate(row)) for y, row in enumerate(g)]

ANIMS = {
    'idle': [draw(), draw(bob=1)],
    'walk': [draw(feet=(1, 0), lean=-1), draw(bob=-1), draw(feet=(0, 1), lean=1), draw(bob=-1)],
    'sleep': [draw(eyes='closed', bob=1, squash=1), draw(eyes='closed', bob=2, squash=1)],
    'poked': [draw(eyes='wide', squash=3), draw(eyes='wide')],
    'throw': [draw(flip=(0, 4), lean=-1), draw(flip=(0, 5), lean=-1, bob=-1), draw(flip=(0, -1), lean=1)],
    'hit': [draw(eyes='hurt', lean=-2, squash=1), draw(eyes='hurt', bob=1)],
    'jump': [draw(squash=3, flip=(-1, -1)), draw(bob=-3, squash=-1, feet=(2, 2), flip=(3, 3))],
    'eat': [draw(mouth='open'), draw(mouth='chew', bob=1, squash=1), draw(eyes='happy', blush=True)],
    'wave': [draw(flip=(0, 3), eyes='happy'), draw(flip=(0, 6), eyes='happy'), draw(flip=(0, 3), eyes='happy')],
    'happy': [draw(eyes='happy', blush=True, bob=-1), draw(eyes='happy', blush=True)],
    'offline': [draw(eyes='hurt', squash=3, bob=1, flip=(-1, -1))],
}

# one horizontal strip PNG per action (1x, 48x48 frames)
def save(name, frames):
    from PIL import Image, ImageColor
    img = Image.new('RGBA', (N * len(frames), N))
    for i, g in enumerate(frames):
        for y, row in enumerate(g):
            for x, c in enumerate(row):
                if c != '.':
                    img.putpixel((i * N + x, y), ImageColor.getrgb(COLORS[c]))
    img.save(name + '.png')

if __name__ == '__main__':
    for name, frames in ANIMS.items():
        save(name, frames)

Add them all at once

Zip the PNGs together and drag the zip onto the editor, or pick it under "Add in bulk", and every slot fills at once. For a plain folder, pick the folder under "Add in bulk". Before adding, it shows you which slot each file goes to, and you can pick a slot yourself for files whose names do not match. The "Download all" zips above use these names, so you can try them straight away.

File names are exactly the action names. Case does not matter, and folders inside the zip are fine.

Check before uploading