gif→sprite sheet

VRChat

GIF to Sprite Sheet for VRChat Animated Emoji

VRChat animated emoji need a 1024×1024 PNG sprite sheet, not a GIF. Grid sizes, frame limits, file naming, upload steps and fixes for sheets that play wrong.

Updated 10 min read

VRChat won’t take a GIF as an emoji. It takes a 1024×1024 PNG with the frames laid out in a 2×2, 4×4 or 8×8 grid, and you tell it how many frames there are and how fast to play them. The quick route: open your GIF in the GIF to sprite sheet tool, switch Made for to VRChat emoji, download the PNG, and upload it at vrchat.com under Inventory → Emoji with Enable Sprite Sheet Mode ticked. You need an active VRC+ subscription to upload custom emoji at all.

I looked for a newer rule before writing this. When the feature shipped (release 2024.2.1, 24 April 2024), VRChat’s web team wrote in the 2 May 2024 developer update that "Animated gifs are not yet supported". I couldn’t find a later release note or wiki edit that says otherwise, and the official wiki still describes sprite sheets only. So for now, converting is the job.

What VRChat accepts

These numbers come from the VRChat wiki’s Emojis and Inventory pages and that developer update.

GridFrame sizeFrames it holdsUsed when the frame count is
2×2512×512 pxup to 44 or fewer
4×4256×256 pxup to 165 to 16
8×8128×128 pxup to 6417 to 64

The sheet is always 1024×1024 and the frames are always square. Frame 1 sits top left, the order runs left to right, then down to the next row. Speed is one number for the whole loop, from 1 to 64 fps. The file has to be under 10 MB and larger than 64×64. Transparency works, both the on/off kind a GIF has and a full alpha channel.

The last column matters more than it looks. VRChat doesn’t inspect your image to find the grid. It slices by the frame count you give it: 4 or fewer means 512 px cells, 16 or fewer means 256 px cells, anything above means 128 px cells. A 17-frame animation therefore has to be drawn on an 8×8 grid, and the 47 leftover cells are simply never shown. Leave them transparent.

One wording trap: the wiki’s Inventory page says emoji "must be smaller than 1024x1024" and, two lines later, that animated emoji sheets should be 1024×1024. Read it as "no larger than".

Make the sheet from your GIF

  1. Open the home tool and drop in the GIF. An animated WebP, APNG, MP4 or WebM works too. Nothing is uploaded, the conversion runs in your browser.
  2. Set Made for to VRChat emoji. The layout controls disappear because VRChat leaves no choices there.
  3. Read the summary box. It shows the grid it picked, how many frames are on the sheet, the pixel size of each, the fps, and the file name.
  4. If you want fewer frames (see the next two sections), drag the Frames range to cut the start or end, or raise Keep every Nth frame. The grid follows the count: 16 or fewer gives you 4×4, 4 or fewer gives 2×2.
  5. Watch the animation preview, then press Download PNG.

The file comes out named like Wave_14frames_20fps.png. That pattern is VRChat’s own convention: the uploader reads the two numbers and sets its frame count and fps fields from them. The preset strips spaces and symbols out of the name part so the pattern stays readable. The fps it writes is the number of frames on the sheet divided by the GIF’s real running time, clamped to VRChat’s 1 to 64 range.

What the preset can’t do: crop to a region, key out a green or solid background, or upload to your account for you. If the GIF has a solid background, the emoji will have one too. Wakamu’s VRCEmoji desktop app (Windows) does chroma key and uploading if you need those.

The command-line way

VRChat’s own suggestion in that developer update was ImageMagick:

montage CoolGif.gif -coalesce -geometry 256x256 -tile 4x4 -background none CoolEmoji_14frames_10fps.png

I ran it with ImageMagick 7.1.2 on a 320×180 test GIF. It does produce a 1024×1024 sheet, pads short animations out to the full 4×4, and centres wide frames in their cells without stretching. For 17 to 64 frames, change the cell and grid:

montage CoolGif.gif -coalesce -geometry 128x128 -tile 8x8 -background none CoolEmoji_40frames_20fps.png

Two things it won’t do. It doesn’t count frames or work out the fps, so the numbers in the file name are yours to type. And it doesn’t drop frames: give the 4×4 command a 28-frame GIF and you get two files, -0.png and -1.png, each a valid-looking sheet holding part of the animation. On my Mac it also refused to start until I added -font with a path to any font file, which has nothing to do with emoji.

Upload it on vrchat.com

  1. Sign in at vrchat.com and go to Inventory → Emoji. Before June 2025 this section was called Gallery, which is why older guides say Gallery → Emoji.
  2. Tick Enable Sprite Sheet Mode before you pick the file. That’s the wiki’s name for it. Several converter sites quote the label with a Beta tag, and I can’t see the page without a VRC+ account, so expect small differences in wording.
  3. Choose your PNG. If the file name follows the pattern, frame count and fps are already filled in. Otherwise set them by hand.
  4. Pick the animation style, the same list ordinary custom emoji use (Bounce, Spin, Orbit, Confetti and so on). This is how the emoji moves through the air, separate from the frames playing inside it.
  5. Pick a loop style. The release notes name one option, Ping Pong, which plays the frames forward and then backward. It’s the fix for a GIF whose last frame doesn’t meet its first.
  6. Save. The emoji appears in the Action Menu under Emojis & Stickers → Emojis and in the Emoji wing of the Quick Menu.

You get 18 custom emoji slots, animated or not. There’s also an in-game route that skips files entirely: set the camera’s mode to Emoji and shoot 4 or 16 photos, which become the frames.

4×4 at 256 px or 8×8 at 128 px

Every sheet is the same 1024×1024, so frames and sharpness come out of one budget. Going from 16 frames to 17 halves the width of each frame and leaves it with a quarter of the pixels.

VRCEmoji’s readme says its 64-frame mode is the best choice in most cases. I’d go the other way for most reaction emoji. A face, a logo or anything with text in it is recognised by its outline, and at 128 px thin lines and small lettering turn to mush. Sixteen frames is plenty for a wave, a blink, a bounce or a spin. I’d only spend the resolution on 8×8 when the motion is the whole point (a dance loop, a clip from a video) and it falls apart below 20 or so frames.

2×2 is for near-stills: a two-frame blink, a four-frame sparkle. At 512 px it’s the only layout that looks crisp up close.

Pixel art changes the sums. The preset enlarges small art by whole numbers only, so pixels stay square: a 32 px sprite becomes 128 px in an 8×8 cell (4×) and 256 px in a 4×4 cell (8×), both clean. A 48 px sprite fits a 128 px cell only at 2×, so it sits at 96 px with a transparent border. Anything larger than the cell is scaled down with smoothing, and pixel art does not survive that well. Shrink the frame count until the cell is at least as big as the art.

When the GIF has more than 64 frames

Sixty-four is a hard ceiling, so something has to go. You can thin the frames across the whole clip or cut the clip shorter, and they fail in different ways.

Thinning is what the preset does by default. It picks 64 frames spread evenly from first to last and lowers the fps so the loop lasts as long as the GIF did. A 90-frame GIF at 30 fps runs 3 seconds; sampled to 64 frames it plays at 21 fps and still runs 3 seconds. Nothing is cut out; the motion is just a little less smooth.

Thinning hard is where it goes wrong. Take that same GIF down to a 4×4 with Keep every 6th frame and you have 15 frames over 3 seconds, which is 5 fps. That’s a slideshow. If you want the 256 px frames, cut instead: use the Frames range to keep the one second that carries the gesture, then thin lightly if you still need to. A lot of reaction GIFs are a second of action followed by a held pose anyway.

Whichever you do, look at where the loop restarts. A cut in the middle of a movement makes a visible jump every cycle. Ping Pong hides that for motions that look fine in reverse (a nod, a sway) and looks strange for ones that don’t (walking, anything falling).

Why wide GIFs get bars

Cells are square and most GIFs aren’t. A 16:9 frame has two ways into a square cell: stretched to fill it, which distorts everything, or scaled to fit the width with empty space above and below. The preset does the second and leaves the space transparent, so in VRChat you see a wide emoji, not a boxed one. The cost is size. A 16:9 frame in a 256 px cell is 256×144, so the subject is drawn at a little over half the height it could have had.

If the subject only fills the middle of a wide GIF, crop the GIF to a square around it before converting (any GIF editor will do, ours doesn’t crop). You get the full cell for the part people look at.

What goes wrong

The emoji shows the whole grid, or doesn’t move

Sprite Sheet Mode wasn’t ticked, so VRChat treated the sheet as one picture. Upload it again with the box checked.

Each frame shows four tiny frames, or a quarter of one

The frame count is on the wrong side of a boundary. Remember that the count picks the cell size. A 4×4 sheet with the count typed as 17 gets sliced into 128 px cells, so you see a corner of each frame. An 8×8 sheet with the count set to 16 gets sliced at 256 px and shows four frames at once. The count has to match the grid the sheet was drawn on: 1 to 4, 5 to 16, or 17 to 64.

It blinks off at the end of every loop

The count is higher than the number of frames actually drawn, so VRChat plays the empty cells. A 14-frame animation on a 4×4 sheet needs the count set to 14, not 16.

It plays too fast or too slow

GIFs store a delay per frame; VRChat plays every frame for the same time. A GIF that holds its last frame for a second loses that pause, and the average speed written in the file name can feel off. Move the fps slider on the upload page until it looks right. If the pause matters, the fix is to repeat the held frame on the sheet, which costs you cells.

Lines of the next frame show at the edges

VRChat’s web team says colour bleed between frames is visible in the website preview and not in the VRChat client. Check in game before redoing the sheet.

The upload is refused

It’s a GIF, it’s bigger than 1024×1024, or it’s over 10 MB. The size limit is rarely the cause: an 8-bit 1024×1024 PNG holds about 4 MB of pixel data before compression. Check the dimensions first, since a sheet exported at 2048 px looks identical in a file browser.

To check a sheet before spending an upload on it, open it in sprite sheet to GIF, enter the rows and columns, click the empty cells to leave them out, and set the fps. If it plays correctly there with the same grid and frame count, the sheet is laid out right.

If you meant Poiyomi or particles

Two other things in VRChat get called a sprite sheet, and the emoji sheet is wrong for the first of them.

Poiyomi flipbook

Poiyomi’s Flipbook section does not take a grid. Its slot takes a Texture2DArray, a stack of separate images. Per the Poiyomi docs you make one inside Unity: import the GIF, right-click it and choose Thry → Gif 2 Texture Array, or select a set of numbered frames and choose Thry → Images 2 Texture Array. Dropping a GIF straight into the slot converts it automatically, and the flipbook’s FPS is set from the GIF. The docs tell you to keep source frames at 512×512 or lower because arrays eat VRAM quickly, and to note the Slices value, which is your frame count.

So for Poiyomi you want the GIF itself, or a folder of frames if you need to drop or reorder some first. The home tool’s ZIP of frames download gives you numbered PNGs for the second route. Guides that tell you to type columns and rows into Poiyomi’s flipbook are describing something the official docs don’t.

Unity particles on an avatar

Particle systems do read a grid. In the Particle System component (Unity 2022.3, the version VRChat’s SDK uses), enable the Texture Sheet Animation module, leave Mode on Grid, and set Tiles X to the number of columns and Y to the number of rows. Animation set to Whole Sheet plays every tile in order, so fill the grid exactly: 16 frames on a 4×4, not 14. Time Mode chooses whether the frames are spread over the particle’s lifetime or played at a fixed FPS, and Cycles repeats the loop within one lifetime.

A VRChat emoji sheet works here if its grid is full. For anything else, use the home tool with Made for on Any engine, a Grid layout and Power-of-two sheet on; it shows the column and row counts to type into Tiles. Particle sheets have no 1024 px or 64-frame rule, though on an avatar a smaller texture is still the kinder choice for everyone who has to download it.