Image Grid Splitter
Split an image into a 3×3 grid for social media sharing
Drag an image here, or click to select a file
Supports JPG, PNG, WebP, BMP, GIF formats
What is an Image Grid Splitter?
The Image Grid Splitter divides one picture into equal tiles, most commonly a 3×3 set of nine images for social media grids. When the pieces are posted in the correct order on platforms such as Instagram, WeChat Moments, or Weibo, the profile or feed view reconstructs the original large image. Creators use this pattern for campaign visuals, product teasers, event announcements, portfolio reveals, and any design where the overall composition matters more than a single post. Good results depend on the source aspect ratio, safe margins for faces and text, and the upload order, because one misplaced tile can break the intended layout.
How to Use
How to use
- Upload or drag and drop an image
- The tool automatically splits it into 9 pieces
- Click any piece to download it individually
- Click "Download All" to save all pieces at once
Cropping Notes
- Use an image with the intended final aspect ratio; automatic splitting cannot recover content already cropped or padded incorrectly.
- Preview all nine pieces before publishing, especially when text, faces, or product edges cross the split lines.
Use Cases
Technical Principle
The image grid splitter is built on the HTML5 Canvas 2D API (WHATWG HTML Living Standard §4.12, originally specified in the W3C HTML5 Recommendation of 2014). The pipeline has three layers: decode the source pixels into a CanvasImageSource, copy a sub-rectangle of those pixels into an offscreen canvas via drawImage's 9-argument form, and then encode the offscreen canvas as a PNG via canvas.toBlob(). Everything happens in the browser tab — no upload, no server, no remote rendering. The decode step matters more than it looks. A File from <input type='file'> or a drag-drop DataTransferItem is read by FileReader.readAsDataURL (or, in modern code, URL.createObjectURL) into a Blob URL, which the browser hands to the Image loader. The Image element triggers 'load' once the bitmap is decoded by the platform's image codec (libpng/libjpeg-turbo/libwebp on Chromium, the equivalent on WebKit/Gecko). A non-CORS image loaded this way is treated as 'tainted' — getImageData would throw a SecurityError — so the splitter only uses drawImage and never reads back pixel arrays, which sidesteps the CORS problem entirely. For very large images (>~8K on the long side) the bitmap is held in GPU memory in some engines, so canvas.toBlob may go through a GPU→CPU readback that costs ~30-100 ms on a phone. The crop is a single drawImage call per slice: ctx.drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh) — the first three pairs are the source rectangle (sx, sy, sw, sh) and the second three are the destination rectangle on the destination canvas. For a 3×3 split of a 1920×1080 photo, each slice's source rectangle is 640×360 starting at (col*640, row*360). The destination canvas is sized exactly to the slice (640×360) and then canvas.toBlob(blob => ..., 'image/png') runs the platform PNG encoder. PNG is chosen over JPEG because grid slices are typically clean rectangular crops with sharp text and UI elements where lossless encoding matters; JPEG's 8×8 DCT blocks would smear edges. The batch download uses URL.createObjectURL on each Blob, an <a download> synthetic click, and revokeObjectURL after a short delay (usually 60-120 s, the platform's lifetime for an unused Blob URL). For 9 slices this fires 9 download events back-to-back; some browsers throttle multiple downloads from the same tab, so the page usually spaces them with a 50-200 ms setTimeout between clicks. Performance-wise, the bottleneck on a phone is usually the PNG encoder (libpng is single-threaded per encode), not drawImage; for 9 1080p slices expect 200-500 ms total on a mid-range Android and 100-200 ms on a desktop Chromium.
- Pipeline: FileReader or URL.createObjectURL → Image element → HTMLCanvasElement → drawImage 9-arg crop → canvas.toBlob('image/png') → URL.createObjectURL + <a download>.
- drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh) is the 9-argument overload that crops a source rectangle (sx,sy,sw,sh) into a destination rectangle (dx,dy,dw,dh); this is the single API call that performs the split.
- Source images that aren't served with CORS headers taint the canvas — getImageData would throw SecurityError, so the splitter never reads pixel arrays, only blits and encodes.
- canvas.toBlob is async and non-blocking (callback fires on a later microtask) versus the legacy canvas.toDataURL which is sync and freezes the main thread for large images.
- For a 3×3 split of an N×M image each slice is (N/3) × (M/3); non-divisible sizes produce a 1-pixel remainder on the right/bottom edge, handled by Math.floor with an explicit remainder slice if needed.
- PNG is chosen over JPEG for grid slices because each slice is a rectangular crop with sharp edges (text, UI, diagrams) where 8×8 DCT blocks would smear the boundary lines.
- Batch download uses URL.createObjectURL per blob + <a download> click; browsers throttle multiple same-tab downloads, so the page usually spaces clicks by 50-200 ms and revokes Blob URLs after the click to free memory.
- Performance bottleneck is the platform PNG encoder (libpng is single-threaded per encode), not drawImage; 9 1080p slices run ~200-500 ms on mid-range mobile and ~100-200 ms on desktop Chromium.
Examples
Square 1:1 split (Instagram post)
Input: 1080 x 1080 px
Output: 9 slices of 360 x 360 px (3x3 grid)
Use: Instagram carousel previews, Xiaohongshu grid postsLandscape 16:9 split (YouTube thumbnail)
Input: 1920 x 1080 px
Output: 9 slices of 640 x 360 px (3x3 grid)
Use: YouTube thumbnail A/B testing, multi-platform banner variantsPortrait 9:16 split (TikTok / Reels)
Input: 1080 x 1920 px
Output: 9 slices of 360 x 640 px (3x3 grid)
Use: short-video cover exploration, story-style previewsFAQ
What does the image grid tool do?
It splits one image into a grid of equal cells (e.g. 3×3, 2×4) so you can post each cell as a separate Instagram-style tile, or assemble them as a puzzle/poster. Each cell is exported as its own PNG/JPEG file.
Is the image uploaded?
No. The grid is computed entirely in your browser via canvas. The image bytes never leave your device.
What grid sizes are supported?
Common presets are 3×3 (Instagram giant tile), 3×1 (banner triptych), 2×2, 4×4, 9×16. Custom rows and columns are usually allowed. The maximum depends on browser memory - very large grids on a high-resolution source can exceed canvas size limits.
How do I post the tiles to Instagram in the right order?
Instagram's grid fills right-to-left, top-to-bottom (so your first post goes top-right, the last to bottom-left for a 3×3). The page exports tiles labelled 1-9 - upload them in order 9, 8, 7, 6, 5, 4, 3, 2, 1 to match the visual layout. Some pages provide an order-hint guide.
Why are my exported tiles slightly mis-aligned?
The source image must have dimensions divisible by the grid size for clean splits. The page either crops the right/bottom edge or scales the image to fit; check which option is selected. If you mounted the tiles physically, sub-pixel alignment errors compound across the grid.
What output format and resolution?
PNG by default at the source resolution. For Instagram, 1080×1080 pixels per tile is the recommended minimum (4320×4320 source for a 4×4 grid). For physical poster prints, use higher source resolution and a print-friendly format like TIFF if your printer supports it.
Can I add gaps between tiles?
Some builds offer a 'gutter' option to leave a white margin around each cell, mimicking how the Instagram grid renders with thin gaps. Adjust gutter width to match the social platform preview if you want the on-screen result to look identical to the source image.