0
# Image Composition
1
2
Sharp provides powerful image composition capabilities for overlaying, blending, and combining multiple images with precise control over positioning and blend modes.
3
4
## Capabilities
5
6
### Image Compositing
7
8
Overlay multiple images with various blend modes and positioning options.
9
10
```javascript { .api }
11
/**
12
* Composite multiple images over the processed image
13
* @param images - Array of overlay specifications
14
* @returns Sharp instance for chaining
15
*/
16
composite(images: OverlayOptions[]): Sharp;
17
18
interface OverlayOptions {
19
/** Input image (Buffer, file path, or creation object) */
20
input: string | Buffer | CreateInput;
21
/** Blend mode for compositing */
22
blend?: BlendMode;
23
/** Gravity-based positioning */
24
gravity?: GravityOption;
25
/** Pixel offset from top edge */
26
top?: number;
27
/** Pixel offset from left edge */
28
left?: number;
29
/** Repeat overlay across entire image */
30
tile?: boolean;
31
/** Overlay is already premultiplied */
32
premultiplied?: boolean;
33
/** DPI for vector overlays */
34
density?: number;
35
/** Read all frames for animated overlays */
36
animated?: boolean;
37
}
38
39
type CreateInput = { create: Create } | { text: CreateText } | { raw: CreateRaw };
40
41
type BlendMode = 'clear' | 'source' | 'over' | 'in' | 'out' | 'atop' | 'dest' |
42
'dest-over' | 'dest-in' | 'dest-out' | 'dest-atop' | 'xor' | 'add' | 'saturate' |
43
'multiply' | 'screen' | 'overlay' | 'darken' | 'lighten' | 'colour-dodge' |
44
'color-dodge' | 'colour-burn' | 'color-burn' | 'hard-light' | 'soft-light' |
45
'difference' | 'exclusion';
46
47
type GravityOption = 'north' | 'northeast' | 'east' | 'southeast' | 'south' |
48
'southwest' | 'west' | 'northwest' | 'center' | 'centre';
49
```
50
51
**Usage Examples:**
52
53
```javascript
54
// Basic overlay
55
await sharp('background.jpg')
56
.composite([{
57
input: 'logo.png',
58
gravity: 'southeast',
59
blend: 'over'
60
}])
61
.toFile('branded.jpg');
62
63
// Multiple overlays with different blend modes
64
await sharp('base.jpg')
65
.composite([
66
{
67
input: 'texture.png',
68
blend: 'multiply',
69
tile: true
70
},
71
{
72
input: 'spotlight.png',
73
blend: 'screen',
74
top: 100,
75
left: 200
76
},
77
{
78
input: 'watermark.png',
79
gravity: 'southeast',
80
blend: 'over'
81
}
82
])
83
.toFile('composite.jpg');
84
85
// Precise positioning
86
await sharp('canvas.jpg')
87
.composite([{
88
input: 'overlay.png',
89
top: 50,
90
left: 100,
91
blend: 'source-over'
92
}])
93
.toFile('positioned.jpg');
94
```
95
96
### Blend Modes
97
98
Different blend modes produce various visual effects when compositing images.
99
100
**Porter-Duff Blend Modes:**
101
- `clear` - Clear destination
102
- `source` - Copy source, ignore destination
103
- `over` - Source over destination (default)
104
- `in` - Source where destination is opaque
105
- `out` - Source where destination is transparent
106
- `atop` - Source over destination, only where destination is opaque
107
- `dest` - Keep destination, ignore source
108
- `dest-over` - Destination over source
109
- `dest-in` - Destination where source is opaque
110
- `dest-out` - Destination where source is transparent
111
- `dest-atop` - Destination over source, only where source is opaque
112
- `xor` - Either source or destination, not both
113
114
**Mathematical Blend Modes:**
115
- `add` - Add source to destination
116
- `saturate` - Saturated addition
117
- `multiply` - Multiply source with destination
118
- `screen` - Screen blend (inverse multiply)
119
- `overlay` - Overlay blend
120
121
**Photographic Blend Modes:**
122
- `darken` - Keep darker pixels
123
- `lighten` - Keep lighter pixels
124
- `colour-dodge` / `color-dodge` - Brighten destination based on source
125
- `colour-burn` / `color-burn` - Darken destination based on source
126
- `hard-light` - Hard light blend
127
- `soft-light` - Soft light blend
128
- `difference` - Absolute difference
129
- `exclusion` - Similar to difference but with lower contrast
130
131
**Usage Examples:**
132
133
```javascript
134
// Multiply blend for shadows/textures
135
await sharp('photo.jpg')
136
.composite([{
137
input: 'shadow-texture.png',
138
blend: 'multiply'
139
}])
140
.toFile('textured.jpg');
141
142
// Screen blend for light effects
143
await sharp('portrait.jpg')
144
.composite([{
145
input: 'light-rays.png',
146
blend: 'screen'
147
}])
148
.toFile('lit.jpg');
149
150
// Difference blend for artistic effects
151
await sharp('image1.jpg')
152
.composite([{
153
input: 'image2.jpg',
154
blend: 'difference'
155
}])
156
.toFile('artistic.jpg');
157
```
158
159
### Tiled Overlays
160
161
Repeat overlays across the entire image surface.
162
163
```javascript { .api }
164
// Tile overlay example
165
await sharp('large-canvas.jpg')
166
.composite([{
167
input: 'pattern.png',
168
tile: true,
169
blend: 'multiply'
170
}])
171
.toFile('patterned.jpg');
172
173
// Tiled watermark
174
await sharp('document.jpg')
175
.composite([{
176
input: 'watermark.png',
177
tile: true,
178
blend: 'over',
179
gravity: 'center'
180
}])
181
.toFile('watermarked.jpg');
182
```
183
184
### Dynamic Content Overlays
185
186
Composite dynamically generated content.
187
188
```javascript { .api }
189
// Text overlay
190
await sharp('photo.jpg')
191
.composite([{
192
input: {
193
text: {
194
text: 'Copyright 2024',
195
font: 'Arial',
196
rgba: true,
197
dpi: 150
198
}
199
},
200
gravity: 'southeast',
201
blend: 'over'
202
}])
203
.toFile('copyrighted.jpg');
204
205
// Generated shape overlay
206
await sharp('background.jpg')
207
.composite([{
208
input: {
209
create: {
210
width: 100,
211
height: 100,
212
channels: 4,
213
background: { r: 255, g: 0, b: 0, alpha: 0.5 }
214
}
215
},
216
top: 50,
217
left: 50,
218
blend: 'over'
219
}])
220
.toFile('with-shape.jpg');
221
```
222
223
### Advanced Composition Patterns
224
225
**Multi-layer Composition:**
226
227
```javascript
228
const createComplexComposite = async (base, elements, output) => {
229
const layers = elements.map(element => ({
230
input: element.file,
231
top: element.y,
232
left: element.x,
233
blend: element.blend || 'over'
234
}));
235
236
await sharp(base)
237
.composite(layers)
238
.toFile(output);
239
};
240
241
// Usage
242
await createComplexComposite('canvas.jpg', [
243
{ file: 'layer1.png', x: 0, y: 0, blend: 'multiply' },
244
{ file: 'layer2.png', x: 100, y: 50, blend: 'screen' },
245
{ file: 'layer3.png', x: 200, y: 100, blend: 'overlay' }
246
], 'composite.jpg');
247
```
248
249
**Conditional Overlays:**
250
251
```javascript
252
const addWatermark = async (input, output, addMark = true) => {
253
const pipeline = sharp(input);
254
255
if (addMark) {
256
pipeline.composite([{
257
input: 'watermark.png',
258
gravity: 'southeast',
259
blend: 'over'
260
}]);
261
}
262
263
await pipeline.toFile(output);
264
};
265
```
266
267
**Batch Processing with Overlays:**
268
269
```javascript
270
const batchOverlay = async (inputDir, overlayFile, outputDir) => {
271
const files = await fs.readdir(inputDir);
272
273
await Promise.all(
274
files.map(file =>
275
sharp(path.join(inputDir, file))
276
.composite([{
277
input: overlayFile,
278
gravity: 'northeast',
279
blend: 'over'
280
}])
281
.toFile(path.join(outputDir, file))
282
)
283
);
284
};
285
```
286
287
**Smart Positioning:**
288
289
```javascript
290
const smartPosition = async (base, overlay, output) => {
291
const { width, height } = await sharp(base).metadata();
292
const { width: overlayWidth, height: overlayHeight } = await sharp(overlay).metadata();
293
294
// Position overlay in bottom-right with margin
295
const margin = 20;
296
const left = width - overlayWidth - margin;
297
const top = height - overlayHeight - margin;
298
299
await sharp(base)
300
.composite([{
301
input: overlay,
302
left,
303
top,
304
blend: 'over'
305
}])
306
.toFile(output);
307
};
308
```
309
310
**Alpha Blending:**
311
312
```javascript
313
const alphaBlend = async (base, overlay, opacity, output) => {
314
// Apply opacity to overlay
315
const transparentOverlay = await sharp(overlay)
316
.ensureAlpha(opacity)
317
.toBuffer();
318
319
await sharp(base)
320
.composite([{
321
input: transparentOverlay,
322
blend: 'over'
323
}])
324
.toFile(output);
325
};
326
```
327
328
**Masked Composition:**
329
330
```javascript
331
const maskedComposite = async (base, overlay, mask, output) => {
332
// Apply mask to overlay
333
const maskedOverlay = await sharp(overlay)
334
.composite([{
335
input: mask,
336
blend: 'dest-in'
337
}])
338
.toBuffer();
339
340
await sharp(base)
341
.composite([{
342
input: maskedOverlay,
343
blend: 'over'
344
}])
345
.toFile(output);
346
};
347
```
348
349
## Constants and Utilities
350
351
```javascript { .api }
352
// Available via sharp.blend (conceptual - blend modes are strings)
353
const blendModes = [
354
'clear', 'source', 'over', 'in', 'out', 'atop',
355
'dest', 'dest-over', 'dest-in', 'dest-out', 'dest-atop', 'xor',
356
'add', 'saturate', 'multiply', 'screen', 'overlay',
357
'darken', 'lighten', 'colour-dodge', 'color-dodge',
358
'colour-burn', 'color-burn', 'hard-light', 'soft-light',
359
'difference', 'exclusion'
360
];
361
362
// Available via sharp.gravity
363
interface GravityEnum {
364
north: number;
365
northeast: number;
366
east: number;
367
southeast: number;
368
south: number;
369
southwest: number;
370
west: number;
371
northwest: number;
372
center: number;
373
centre: number;
374
}
375
```
376
377
**Usage Examples:**
378
379
```javascript
380
// Using gravity constants
381
await sharp('base.jpg')
382
.composite([{
383
input: 'overlay.png',
384
gravity: sharp.gravity.northeast
385
}])
386
.toFile('positioned.jpg');
387
```
388
389
## Performance Considerations
390
391
- **Large Images**: Be mindful of memory usage when compositing large images
392
- **Multiple Overlays**: Each overlay adds processing time; consider combining overlays when possible
393
- **Blend Modes**: Some blend modes are more computationally expensive than others
394
- **Tiled Overlays**: Can be memory-intensive for large canvases with small tiles
395
- **Dynamic Content**: Text and shape generation adds overhead; cache when possible