Seedream 5.5

Seedream 5.5 is ByteDance's image generation and editing model, focused on visual realism, aesthetics and editing consistency. It supports text-to-image and reference-guided editing with up to 10 input images, keeps unedited areas stable across multi-turn edits, and outputs transparent images from a prompt or from references. It can also split an image into a base image and up to 99 editable layers, returning the text, position, font and style of each text layer, which suits poster, packaging and marketing design workflows.

Complete technical specification for integration
Ready-to-use code snippets for common workflows
Step-by-step tutorials for advanced use cases
← All GuidesLayer decomposition
How to split one image into a base image and transparent layers with Seedream 5.5, from layer positions and split size to reading the text on text layers.
Introduction
A finished design is one flat bitmap. Moving the headline or translating the copy means regenerating the whole image and hoping the rest survives, or rebuilding it by hand in a design tool.
settings.layerDecomposition turns that bitmap back into parts. Seedream 5.5 takes one image and returns an opaque base image plus transparent PNG layers, each with its position on the canvas, its place in the stack, and a short name and description.

A flat children's picture-book illustration in gouache with visible paper texture. A girl of about seven in a bright yellow raincoat and red rubber boots runs across a rounded green hill from left to right, holding the string of a red diamond-shaped kite that flies high in the upper left of the sky. A small scruffy brown dog runs beside her with its ears flapping. A large oak tree with a round dark green canopy stands on the right edge of the hill. Soft white clouds drift across a pale blue sky. Warm, friendly palette, clean shapes with clear space between the girl, the dog, the kite and the tree. No text.





Each part arrives as its own file, so an animator can move the dog without touching the girl, and a layout can push the kite higher without regenerating the page.
This guide covers the request, what each layer carries, putting the layers back together, setting how finely the image splits, steering the split with a prompt and reading the text on text layers.
The request
A decomposition request is one reference image and one setting. The prompt is optional.
import { createClient } from '@runware/sdk'
const client = await createClient({ apiKey: process.env.RUNWARE_API_KEY })
await client.connect()
const [result] = await client.run({
model: 'bytedance:seedream@5.5',
inputs: {
referenceImages: [
'https://assets.runware.ai/guides/bytedance-seedream-5-5/layer-decomposition/source-kite.jpg'
]
},
settings: {
layerDecomposition: true,
layerDecompositionMaxImages: 6
},
outputFormat: 'PNG'
})import asyncio
import os
from runware import Runware
async def main():
async with Runware(api_key=os.environ["RUNWARE_API_KEY"]) as client:
results = await client.run({
"model": "bytedance:seedream@5.5",
"inputs": {
"referenceImages": [
"https://assets.runware.ai/guides/bytedance-seedream-5-5/layer-decomposition/source-kite.jpg"
]
},
"settings": {
"layerDecomposition": True,
"layerDecompositionMaxImages": 6
},
"outputFormat": "PNG"
})
asyncio.run(main())curl https://api.runware.ai/v1 \
-H "Authorization: Bearer $RUNWARE_API_KEY" \
-H "Content-Type: application/json" \
-d '[
{
"taskType": "imageInference",
"taskUUID": "4c8e1f2a-7b3d-4e9a-a1c6-5d2f8b0e9c37",
"model": "bytedance:seedream@5.5",
"inputs": {
"referenceImages": [
"https://assets.runware.ai/guides/bytedance-seedream-5-5/layer-decomposition/source-kite.jpg"
]
},
"settings": {
"layerDecomposition": true,
"layerDecompositionMaxImages": 6
},
"outputFormat": "PNG"
}
]'runware run bytedance:seedream@5.5 \
inputs.referenceImages.0=https://assets.runware.ai/guides/bytedance-seedream-5-5/layer-decomposition/source-kite.jpg \
settings.layerDecomposition=true \
settings.layerDecompositionMaxImages=6 \
outputFormat=PNG{
"taskType": "imageInference",
"taskUUID": "4c8e1f2a-7b3d-4e9a-a1c6-5d2f8b0e9c37",
"model": "bytedance:seedream@5.5",
"inputs": {
"referenceImages": [
"https://assets.runware.ai/guides/bytedance-seedream-5-5/layer-decomposition/source-kite.jpg"
]
},
"settings": {
"layerDecomposition": true,
"layerDecompositionMaxImages": 6
},
"outputFormat": "PNG"
}Response
[
{
"taskType": "imageInference",
"taskUUID": "4c8e1f2a-7b3d-4e9a-a1c6-5d2f8b0e9c37",
"imageUUID": "9a3f6c2e-1d4b-4e8a-b7c5-0f2e8d6a4b13",
"imageURL": "https://im.runware.ai/image/os/a14d18/ws/2/ii/9a3f6c2e-1d4b-4e8a-b7c5-0f2e8d6a4b13.png",
"outputs": {
"layers": [
{
"uuid": "819e7e67-b36d-4b20-9a42-7584f8027824",
"url": "https://im.runware.ai/image/os/a14d18/ws/2/ii/819e7e67-b36d-4b20-9a42-7584f8027824.png",
"zIndex": 1,
"boundingBox": {
"absolute": [0, 115, 1257, 1110],
"normalized": [0, 56, 614, 542]
},
"name": "Red diamond kite with ribbons",
"description": "A red diamond-shaped kite fitted with red-orange two-tone streamers and a pink kite line."
},
{
"uuid": "14941738-c0ca-4a3f-b764-e0746bc85a34",
"url": "https://im.runware.ai/image/os/a14d18/ws/2/ii/14941738-c0ca-4a3f-b764-e0746bc85a34.png",
"zIndex": 2,
"boundingBox": {
"absolute": [528, 710, 1178, 1563],
"normalized": [258, 347, 575, 763]
},
"name": "Running little girl holding kite line",
"description": "A running little girl wearing a yellow hooded coat, a white dress with red floral patterns, and red rain boots, holding the kite line in her hand."
}
]
}
}
]- Exactly one image goes in
inputs.referenceImages, as a PNG or a JPEG holding at least 262,144 pixels, the area of 512 × 512. positivePromptcan be left out, and then the model plans the split on its own. A prompt only steers which parts become layers, as shown in Steering the split with a prompt.widthandheightare rejected in this mode, and so issettings.backgroundwith any value, because the base is always opaque and every layer transparent.resolutiontakes a tier. Leave it out and the output keeps the size of the source when the source holds between 921,600 and 4,624,220 pixels. A smaller source comes back at 1K and a larger one at 2K, both with the source's aspect ratio.
outputFormat sets the format of the base image only. The layers are always PNG, since they carry an alpha channel.
What each layer carries
The base image comes back where every image result does, in imageURL. The layers come back in outputs.layers, one entry per layer with its file and where it belongs.
| Field | What it holds |
|---|---|
imageURL | The base image, opaque, at the bottom of the stack |
outputs.layers[].url | One part of the image as a PNG with an alpha channel |
outputs.layers[].zIndex | The stacking order. The base sits at 0, and a higher number draws on top |
outputs.layers[].boundingBox.absolute | The layer's box on the base image in pixels, as [left, top, right, bottom] |
outputs.layers[].boundingBox.normalized | The same box on a 0 to 1000 scale |
outputs.layers[].name | A short label written by the model |
outputs.layers[].description | A sentence describing what the layer shows |
Each layer file holds only its own box, at its own resolution. A part that covers about a megapixel or more comes back at the size of its box, and a smaller one comes back enlarged: the dog's box above is 507 × 405 pixels, and its PNG is 1146 × 915. Scale each layer to the size of its boundingBox.absolute before placing it, and use normalized to place it on a canvas of a different size.
name and description are written by the model for each image. They help match a layer to a role in a template, such as finding the headline or the product, but they are free text rather than a fixed vocabulary, so match them loosely. Their language is not fixed either. A layer that merges several parts, which happens more at a low maximum, can get a description partly in Chinese, so translate or clean the text before showing it to a user.
Putting the layers back together
Draw the base, then each layer scaled to its box and placed at the box's left and top, taken from boundingBox.absolute, in zIndex order. The stack rebuilds the source.
With the response array in tasks, sharp does the whole stack in one call:
import sharp from 'sharp'
const download = async (url: string) => Buffer.from(await (await fetch(url)).arrayBuffer())
const [result] = tasks
const layers = [...result.outputs.layers].sort((a, b) => a.zIndex - b.zIndex)
const rebuilt = await sharp(await download(result.imageURL))
.composite(await Promise.all(layers.map(async (layer) => {
const [left, top, right, bottom] = layer.boundingBox.absolute
const input = await sharp(await download(layer.url))
.resize(right - left, bottom - top, { fit: 'fill' })
.toBuffer()
return { input, left, top }
})))
.png()
.toBuffer()Change a layer's left and top before compositing to move it, or skip a layer to take it out of the picture. The base fills in what the layers covered, so taking a part out leaves backdrop rather than a hole.

Setting the number of layers
settings.layerDecompositionMaxImages caps the images in the response, the base included, so 4 means a base and at most three layers. Left out, it is 30.
The same social ad split twice, once with a maximum of 4 and once with 16:

A flat-lay social media ad for a granola brand, shot from directly above on a soft peach background. At the center, a white ceramic bowl of golden oat granola topped with fresh blueberries and sliced strawberries, a wooden spoon resting against it. Around the bowl, scattered rolled oats, whole almonds, single blueberries and two halved strawberries, with a folded linen napkin in the lower right. At the top, a bold headline in deep green sans-serif reads "BREAKFAST, SORTED". At the bottom, a rounded deep green button with white text reads "SHOP NOW". Bright natural daylight, crisp commercial food photography.


A higher maximum gives a finer split. At 4 the model returned three layers and grouped parts that sit near each other: the bowl with the button under it, and the logo with the napkin and the board. At 16 it returned eight, with the headline, the logo, the bowl, two strawberries, the napkin, the board and the button each on its own, and the scattered oats, almonds and blueberries left in the base.
The maximum is a ceiling, not a target, and the model can return fewer layers than it allows. A maximum below the number of parts also splits less cleanly: at 4, the base kept its own copy of the logo, the button and the board.
At a maximum that low, the grouping also changes from run to run, so a retry can come back split differently. Two more runs at 4 kept the headline on its own but put the button with the napkin instead of the bowl, and one of them put the logo with the bowl. With room for every part, three runs of the room in the next section returned the same six layers, and only the wording of name and description changed.
Every image in the response is billed as a separate output image, the base and each layer, so the maximum also caps what a request can cost. Set it to the number of parts your edit needs, plus one for the base.
Steering the split with a prompt
Without a prompt, the model plans the split from the image and the maximum. A prompt that names the parts takes over that plan. The room below was split twice with the same maximum of 10.

Photorealistic real estate listing photograph of a bright modern living room. A deep green velvet three-seat sofa with two mustard cushions stands against a plain white wall. A round light oak coffee table sits on a cream wool rug in front of it, a black metal floor lamp stands to the left of the sofa, and a woven rattan armchair is on the right. A large window with sheer white curtains fills the left wall. Light oak floorboards. Wide straight-on shot at eye level with a 24mm lens, bright natural daylight, crisp detail.


Separate only the sofa, together with its cushions, into its own layer.

Separate only the sofa, together with its cushions, into its own layer.
Without a prompt the room came back as six layers over an empty room. The prompted split returns one layer for the sofa and a base with the rest of the room, which is the starting point for virtual staging: drop a different sofa onto the base, or restyle the sofa layer and put it back.
Name every part you want as its own layer, and leave the base out of the prompt, since everything you do not name stays there. Ending a prompt on this room with "Leave everything else in the base image" returned, in four runs out of five, an extra opaque layer that covers the whole frame and nearly repeats the base, billed as one more image.
Keep the maximum above the number of parts you name, because a prompt that asks for more layers than the maximum allows loses some of what it names.
A part can also be pointed at by its position instead of its name, which suits an editor where the user drags a box over it. Put the box in the prompt between <bbox> tags, as left, top, right and bottom on the 0 to 1000 scale of boundingBox.normalized:
Separate the regions at <bbox>308 484 438 570</bbox> and <bbox>566 484 694 570</bbox> into two separate layers, one per region.On this room the two boxes returned each mustard cushion as its own layer, the same split that naming the cushions gave, with the sofa filled in behind them on the base.
Reading text with text recognition
settings.layerDecompositionTextRecognition adds a textRecognition object to every layer the model reads as typeset text: the full string, the box of each line, and runs of characters that share a style. It turns a flat flyer into copy a localization pipeline can translate and set again.

A printed promotional flyer for a taco restaurant, flat graphic design on a deep teal background. At the very top, the restaurant name "LA PALMA TAQUERIA" in small bold white sans-serif capitals. Below it, a huge bold condensed sans-serif headline in sunflower yellow reads "TACO TUESDAY". Under the headline, one line in bold sans-serif where "$2 TACOS" is bright red and "ALL NIGHT" is white. In the middle, a photo of three street tacos with cilantro, onion and lime wedges on a round white plate. Below the photo, a line in white italic sans-serif reads "Every Tuesday, 5 PM to close". Beside the plate, a hand-lettered brush script in coral reads "¡Buen provecho!". At the bottom, a white underlined sans-serif line reads "lapalmataqueria.com". Clean margins, crisp print-ready typography.


The price line comes back as one layer with two runs, because the color changes partway through it:
import { createClient } from '@runware/sdk'
const client = await createClient({ apiKey: process.env.RUNWARE_API_KEY })
await client.connect()
const [result] = await client.run({
model: 'bytedance:seedream@5.5',
inputs: {
referenceImages: [
'https://assets.runware.ai/guides/bytedance-seedream-5-5/layer-decomposition/source-flyer.jpg'
]
},
settings: {
layerDecomposition: true,
layerDecompositionMaxImages: 12,
layerDecompositionTextRecognition: true
},
outputFormat: 'PNG'
})import asyncio
import os
from runware import Runware
async def main():
async with Runware(api_key=os.environ["RUNWARE_API_KEY"]) as client:
results = await client.run({
"model": "bytedance:seedream@5.5",
"inputs": {
"referenceImages": [
"https://assets.runware.ai/guides/bytedance-seedream-5-5/layer-decomposition/source-flyer.jpg"
]
},
"settings": {
"layerDecomposition": True,
"layerDecompositionMaxImages": 12,
"layerDecompositionTextRecognition": True
},
"outputFormat": "PNG"
})
asyncio.run(main())curl https://api.runware.ai/v1 \
-H "Authorization: Bearer $RUNWARE_API_KEY" \
-H "Content-Type: application/json" \
-d '[
{
"taskType": "imageInference",
"taskUUID": "b6d2a8f4-3e1c-4a97-9c05-7f8e2d1b4a63",
"model": "bytedance:seedream@5.5",
"inputs": {
"referenceImages": [
"https://assets.runware.ai/guides/bytedance-seedream-5-5/layer-decomposition/source-flyer.jpg"
]
},
"settings": {
"layerDecomposition": true,
"layerDecompositionMaxImages": 12,
"layerDecompositionTextRecognition": true
},
"outputFormat": "PNG"
}
]'runware run bytedance:seedream@5.5 \
inputs.referenceImages.0=https://assets.runware.ai/guides/bytedance-seedream-5-5/layer-decomposition/source-flyer.jpg \
settings.layerDecomposition=true \
settings.layerDecompositionMaxImages=12 \
settings.layerDecompositionTextRecognition=true \
outputFormat=PNG{
"taskType": "imageInference",
"taskUUID": "b6d2a8f4-3e1c-4a97-9c05-7f8e2d1b4a63",
"model": "bytedance:seedream@5.5",
"inputs": {
"referenceImages": [
"https://assets.runware.ai/guides/bytedance-seedream-5-5/layer-decomposition/source-flyer.jpg"
]
},
"settings": {
"layerDecomposition": true,
"layerDecompositionMaxImages": 12,
"layerDecompositionTextRecognition": true
},
"outputFormat": "PNG"
}Response
[
{
"taskType": "imageInference",
"taskUUID": "b6d2a8f4-3e1c-4a97-9c05-7f8e2d1b4a63",
"imageUUID": "725a544b-0fe7-4099-ac45-71432b56a900",
"imageURL": "https://im.runware.ai/image/os/a14d18/ws/2/ii/725a544b-0fe7-4099-ac45-71432b56a900.png",
"outputs": {
"layers": [
{
"uuid": "04890230-b75b-4245-b81a-d6445a66dc70",
"url": "https://im.runware.ai/image/os/a14d18/ws/2/ii/04890230-b75b-4245-b81a-d6445a66dc70.png",
"zIndex": 3,
"boundingBox": {
"absolute": [200, 301, 1334, 685],
"normalized": [130, 147, 868, 334]
},
"name": "Red bold $2 TACOS and white bold ALL NIGHT",
"description": "Bold red text \"$2 TACOS\" paired with bold white text \"ALL NIGHT\"",
"textRecognition": {
"text": "$2 TACOS ALL NIGHT",
"lines": [
{
"runs": [
{
"text": "$2 TACOS ",
"style": {
"similarFontFamily": "Indivisible",
"foregroundColor": "#EB3234",
"bold": true,
"italic": false,
"underline": false,
"strikethrough": false,
"shadow": false,
"translucent": false
}
},
{
"text": "ALL NIGHT",
"style": {
"similarFontFamily": "Indivisible",
"foregroundColor": "#F4F4F2",
"bold": true,
"italic": false,
"underline": false,
"strikethrough": false,
"shadow": false,
"translucent": false
}
}
],
"boundingBox": {
"absolute": [354, 442, 1178, 544],
"normalized": [230, 216, 767, 266]
}
}
]
}
}
]
}
}
]textis the whole layer as plain text, with lines joined by\n, ready to send to translation. The hours line and the web address on this flyer came back as one layer, so itstextholds both lines.lines[].boundingBoxplaces each line on the base, in the sameabsoluteandnormalizedform as a layer's box.runs[]splits a line wherever the style changes, and eachstyleholds the font, the color as#RRGGBBand six flags:bold,italic,underline,strikethrough,shadowandtranslucent.
To localize the flyer, translate text, then set each line in its box on the base, in a font close to similarFontFamily and the run's foregroundColor. The image layers stay as they are.
similarFontFamily is the closest match from the fonts the model recognizes, not the font the design used, and it reads unknown when the model is not confident. Map it to a font you license before setting type.
The style flags describe the letters themselves. A layer rotated on the canvas does not read as italic, and a solid light color does not read as translucent. Check the flags against the image before reusing them: the web address on this flyer is underlined, and it came back with underline set to false here and on an earlier version of the same flyer.
Only typeset text gets textRecognition. Hand-lettered and artistic text get none, and small or heavily stylized type may get none either, so the brush script on the flyer comes back as an image layer without it. Check for the field before reading it.
Tips
-
Name the parts you want. Without a prompt the model decides the split. A prompt that lists the parts gets that split instead, and everything it does not name stays in the base.
-
Size the maximum to the edit. It counts the base, and every image that comes back is billed, so set it to the parts you need plus one.
-
Read the layer count from the response. The model can return fewer layers than the maximum, so loop over
outputs.layersinstead of expecting a fixed number. -
Scale each layer to its box. A small part comes back enlarged, so resize it to the size of
boundingBox.absolutebefore placing it at the box's left and top. -
Check for text recognition first. Only layers read as typeset text carry
textRecognition, so test for it before reading it. -
Treat the font and the flags as hints. Map
similarFontFamilyto a licensed font, and check the style flags against the image. -
Retry the whole request when it fails. If one layer fails to generate, the request fails, and nothing partial comes back.

