Cloudinary Blog

Transform your image overlays with on-the-fly manipulation

Dynamically Add and Manipulate Multiple Image Overlays

Front end developers may want to combine multiple images into a single image. For example, when creating and adding watermarks to stock photos, adding shapes or badges, preparing content for print (e.g. placing a logo on a t-shirt or a mug), adding a caption, and so on.

Multiple images can be combined by overlaying them one on top of the other. However, since it is not a given that both the underlying and overlaying images match each other, let alone your graphic design, you may need to perform further image manipulations (e.g. resize, crop, change colors, create a better fit). This is where Cloudinary comes in.

Cloudinary's image overlay feature helps users easily combine multiple images. It supports image and text overlays using on-the-fly manipulation URLs. In this blog post, we will show you how to separately manipulate, process, and transform underlying and overlaying images, then dynamically generate a resulting image that you can embed on your site.

Webinar
How to Optimize for Page Load Speed

Manipulating image overlays

Suppose you have a website that sells personalized gifts. Users can file upload their own photos, add text, and your site will automatically crop and manipulate those photos and text on the gift of their choice. For example, a couple may want to place their picture on a coffee mug. This would require you to resize and manipulate both the underlying image of the coffee mug and the overlaying picture of the couple until they fit together in harmony. Once the images are put in place, you can add text and perform further manipulations if necessary.

Below is an example of the original images of the couple and coffee mug that were uploaded to the cloud for further manipulation and delivery.

Coffee cup Nice couple

You can add an image overlay using Cloudinary's overlay parameter (or l for URLs). Returning to our example, here is what the final version of the coffee mug would look like with the overlaying picture of the couple:

Ruby:
Copy to clipboard
cl_image_tag("coffee_cup.jpg", :transformation=>[
  {:width=>400, :height=>250, :gravity=>"south", :crop=>"fill"},
  {:overlay=>"nice_couple", :width=>90, :gravity=>"center", :y=>18, :x=>-20}
  ])
PHP v1:
Copy to clipboard
cl_image_tag("coffee_cup.jpg", array("transformation"=>array(
  array("width"=>400, "height"=>250, "gravity"=>"south", "crop"=>"fill"),
  array("overlay"=>"nice_couple", "width"=>90, "gravity"=>"center", "y"=>18, "x"=>-20)
  )))
PHP v2:
Copy to clipboard
(new ImageTag('coffee_cup.jpg'))
  ->resize(Resize::fill()->width(400)->height(250)->gravity(Gravity::compass(Compass::south())))
  ->overlay(
      Overlay::source(Source::image('nice_couple')
        ->transformation((new ImageTransformation())
          ->resize(Resize::scale()->width(90))))
      ->position((new Position())
        ->gravity(Gravity::compass(Compass::center()))
        ->offsetX(-20)->offsetY(18)
  ));
Python:
Copy to clipboard
CloudinaryImage("coffee_cup.jpg").image(transformation=[
  {'width': 400, 'height': 250, 'gravity': "south", 'crop': "fill"},
  {'overlay': "nice_couple", 'width': 90, 'gravity': "center", 'y': 18, 'x': -20}
  ])
Node.js:
Copy to clipboard
cloudinary.image("coffee_cup.jpg", {transformation: [
  {width: 400, height: 250, gravity: "south", crop: "fill"},
  {overlay: "nice_couple", width: 90, gravity: "center", y: 18, x: -20}
  ]})
Java:
Copy to clipboard
cloudinary.url().transformation(new Transformation()
  .width(400).height(250).gravity("south").crop("fill").chain()
  .overlay(new Layer().publicId("nice_couple")).width(90).gravity("center").y(18).x(-20)).imageTag("coffee_cup.jpg");
JS:
Copy to clipboard
cloudinary.imageTag('coffee_cup.jpg', {transformation: [
  {width: 400, height: 250, gravity: "south", crop: "fill"},
  {overlay: new cloudinary.Layer().publicId("nice_couple"), width: 90, gravity: "center", y: 18, x: -20}
  ]}).toHtml();
jQuery:
Copy to clipboard
$.cloudinary.image("coffee_cup.jpg", {transformation: [
  {width: 400, height: 250, gravity: "south", crop: "fill"},
  {overlay: new cloudinary.Layer().publicId("nice_couple"), width: 90, gravity: "center", y: 18, x: -20}
  ]})
React:
Copy to clipboard
<Image publicId="coffee_cup.jpg" >
  <Transformation width="400" height="250" gravity="south" crop="fill" />
  <Transformation overlay="nice_couple" width="90" gravity="center" y="18" x="-20" />
</Image>
Vue.js:
Copy to clipboard
<cld-image publicId="coffee_cup.jpg" >
  <cld-transformation width="400" height="250" gravity="south" crop="fill" />
  <cld-transformation :overlay="nice_couple" width="90" gravity="center" y="18" x="-20" />
</cld-image>
Angular:
Copy to clipboard
<cl-image public-id="coffee_cup.jpg" >
  <cl-transformation width="400" height="250" gravity="south" crop="fill">
  </cl-transformation>
  <cl-transformation overlay="nice_couple" width="90" gravity="center" y="18" x="-20">
  </cl-transformation>
</cl-image>
.NET:
Copy to clipboard
cloudinary.Api.UrlImgUp.Transform(new Transformation()
  .Width(400).Height(250).Gravity("south").Crop("fill").Chain()
  .Overlay(new Layer().PublicId("nice_couple")).Width(90).Gravity("center").Y(18).X(-20)).BuildImageTag("coffee_cup.jpg")
Android:
Copy to clipboard
MediaManager.get().url().transformation(new Transformation()
  .width(400).height(250).gravity("south").crop("fill").chain()
  .overlay(new Layer().publicId("nice_couple")).width(90).gravity("center").y(18).x(-20)).generate("coffee_cup.jpg");
iOS:
Copy to clipboard
imageView.cldSetImage(cloudinary.createUrl().setTransformation(CLDTransformation()
  .setWidth(400).setHeight(250).setGravity("south").setCrop("fill").chain()
  .setOverlay("nice_couple").setWidth(90).setGravity("center").setY(18).setX(-20)).generate("coffee_cup.jpg")!, cloudinary: cloudinary)
Image overlay

Transformation instructions are used to perform the image manipulations using dynamic URLs. Cloudinary's client libraries assist in building these URLs. You can apply multiple chained transformations by separating them with a slash / in your image's URL.

In order to better manipulate image overlays, you can set the flags parameter to layer_apply (or fl_layer_apply for URLs), which then tells Cloudinary that all chained transformations that were specified up until the flag, are to be applied on the overlaying image instead of the containing image.

Using our coffee mug example, below you can see how we have applied multiple manipulations both on the containing image as well as the overlay. The containing image has been cropped to fill a 400x250 rectangle and the overlaying image of the couple has been cropped using face detection. Color saturation has been increased by 50% and the vignette effect has been applied. Finally, the resulting image has been resized to 100 pixels wide, converted to a circular shape and positioned with 20 pixels offset from the center of the containing image.

Ruby:
Copy to clipboard
cl_image_tag("coffee_cup.jpg", :transformation=>[
  {:width=>400, :height=>250, :gravity=>"south", :crop=>"fill"},
  {:overlay=>"nice_couple", :width=>1.3, :height=>1.3, :gravity=>"faces", :flags=>"region_relative", :crop=>"crop"},
  {:effect=>"saturation:50"},
  {:effect=>"vignette"},
  {:flags=>"layer_apply", :width=>100, :radius=>"max", :gravity=>"center", :y=>20, :x=>-20, :crop=>"scale"}
  ])
PHP v1:
Copy to clipboard
cl_image_tag("coffee_cup.jpg", array("transformation"=>array(
  array("width"=>400, "height"=>250, "gravity"=>"south", "crop"=>"fill"),
  array("overlay"=>"nice_couple", "width"=>"1.3", "height"=>"1.3", "gravity"=>"faces", "flags"=>"region_relative", "crop"=>"crop"),
  array("effect"=>"saturation:50"),
  array("effect"=>"vignette"),
  array("flags"=>"layer_apply", "width"=>100, "radius"=>"max", "gravity"=>"center", "y"=>20, "x"=>-20, "crop"=>"scale")
  )))
PHP v2:
Copy to clipboard
(new ImageTag('coffee_cup.jpg'))
  ->resize(Resize::fill()->width(400)->height(250)->gravity(Gravity::compass(Compass::south())))
  ->overlay(
      Overlay::source(Source::image('nice_couple')
        ->transformation((new ImageTransformation())
          ->resize(Resize::crop()->width(1.3)->height(1.3)
            ->gravity(Gravity::focusOn(FocusOn::faces()))
            ->regionRelative())
          ->adjust(Adjust::saturation()->level(50))
          ->effect(Effect::vignette())
          ->resize(Resize::scale()->width(100))
          ->roundCorners(RoundCorners::max())))
      ->position((new Position())
        ->gravity(Gravity::compass(Compass::center()))
        ->offsetX(-20)->offsetY(20)
  ));
Python:
Copy to clipboard
CloudinaryImage("coffee_cup.jpg").image(transformation=[
  {'width': 400, 'height': 250, 'gravity': "south", 'crop': "fill"},
  {'overlay': "nice_couple", 'width': "1.3", 'height': "1.3", 'gravity': "faces", 'flags': "region_relative", 'crop': "crop"},
  {'effect': "saturation:50"},
  {'effect': "vignette"},
  {'flags': "layer_apply", 'width': 100, 'radius': "max", 'gravity': "center", 'y': 20, 'x': -20, 'crop': "scale"}
  ])
Node.js:
Copy to clipboard
cloudinary.image("coffee_cup.jpg", {transformation: [
  {width: 400, height: 250, gravity: "south", crop: "fill"},
  {overlay: "nice_couple", width: "1.3", height: "1.3", gravity: "faces", flags: "region_relative", crop: "crop"},
  {effect: "saturation:50"},
  {effect: "vignette"},
  {flags: "layer_apply", width: 100, radius: "max", gravity: "center", y: 20, x: -20, crop: "scale"}
  ]})
Java:
Copy to clipboard
cloudinary.url().transformation(new Transformation()
  .width(400).height(250).gravity("south").crop("fill").chain()
  .overlay(new Layer().publicId("nice_couple")).width(1.3).height(1.3).gravity("faces").flags("region_relative").crop("crop").chain()
  .effect("saturation:50").chain()
  .effect("vignette").chain()
  .flags("layer_apply").width(100).radius("max").gravity("center").y(20).x(-20).crop("scale")).imageTag("coffee_cup.jpg");
JS:
Copy to clipboard
cloudinary.imageTag('coffee_cup.jpg', {transformation: [
  {width: 400, height: 250, gravity: "south", crop: "fill"},
  {overlay: new cloudinary.Layer().publicId("nice_couple"), width: "1.3", height: "1.3", gravity: "faces", flags: "region_relative", crop: "crop"},
  {effect: "saturation:50"},
  {effect: "vignette"},
  {flags: "layer_apply", width: 100, radius: "max", gravity: "center", y: 20, x: -20, crop: "scale"}
  ]}).toHtml();
jQuery:
Copy to clipboard
$.cloudinary.image("coffee_cup.jpg", {transformation: [
  {width: 400, height: 250, gravity: "south", crop: "fill"},
  {overlay: new cloudinary.Layer().publicId("nice_couple"), width: "1.3", height: "1.3", gravity: "faces", flags: "region_relative", crop: "crop"},
  {effect: "saturation:50"},
  {effect: "vignette"},
  {flags: "layer_apply", width: 100, radius: "max", gravity: "center", y: 20, x: -20, crop: "scale"}
  ]})
React:
Copy to clipboard
<Image publicId="coffee_cup.jpg" >
  <Transformation width="400" height="250" gravity="south" crop="fill" />
  <Transformation overlay="nice_couple" width="1.3" height="1.3" gravity="faces" flags="region_relative" crop="crop" />
  <Transformation effect="saturation:50" />
  <Transformation effect="vignette" />
  <Transformation flags="layer_apply" width="100" radius="max" gravity="center" y="20" x="-20" crop="scale" />
</Image>
Vue.js:
Copy to clipboard
<cld-image publicId="coffee_cup.jpg" >
  <cld-transformation width="400" height="250" gravity="south" crop="fill" />
  <cld-transformation :overlay="nice_couple" width="1.3" height="1.3" gravity="faces" flags="region_relative" crop="crop" />
  <cld-transformation effect="saturation:50" />
  <cld-transformation effect="vignette" />
  <cld-transformation flags="layer_apply" width="100" radius="max" gravity="center" y="20" x="-20" crop="scale" />
</cld-image>
Angular:
Copy to clipboard
<cl-image public-id="coffee_cup.jpg" >
  <cl-transformation width="400" height="250" gravity="south" crop="fill">
  </cl-transformation>
  <cl-transformation overlay="nice_couple" width="1.3" height="1.3" gravity="faces" flags="region_relative" crop="crop">
  </cl-transformation>
  <cl-transformation effect="saturation:50">
  </cl-transformation>
  <cl-transformation effect="vignette">
  </cl-transformation>
  <cl-transformation flags="layer_apply" width="100" radius="max" gravity="center" y="20" x="-20" crop="scale">
  </cl-transformation>
</cl-image>
.NET:
Copy to clipboard
cloudinary.Api.UrlImgUp.Transform(new Transformation()
  .Width(400).Height(250).Gravity("south").Crop("fill").Chain()
  .Overlay(new Layer().PublicId("nice_couple")).Width(1.3).Height(1.3).Gravity("faces").Flags("region_relative").Crop("crop").Chain()
  .Effect("saturation:50").Chain()
  .Effect("vignette").Chain()
  .Flags("layer_apply").Width(100).Radius("max").Gravity("center").Y(20).X(-20).Crop("scale")).BuildImageTag("coffee_cup.jpg")
Android:
Copy to clipboard
MediaManager.get().url().transformation(new Transformation()
  .width(400).height(250).gravity("south").crop("fill").chain()
  .overlay(new Layer().publicId("nice_couple")).width(1.3).height(1.3).gravity("faces").flags("region_relative").crop("crop").chain()
  .effect("saturation:50").chain()
  .effect("vignette").chain()
  .flags("layer_apply").width(100).radius("max").gravity("center").y(20).x(-20).crop("scale")).generate("coffee_cup.jpg");
iOS:
Copy to clipboard
imageView.cldSetImage(cloudinary.createUrl().setTransformation(CLDTransformation()
  .setWidth(400).setHeight(250).setGravity("south").setCrop("fill").chain()
  .setOverlay("nice_couple").setWidth(1.3).setHeight(1.3).setGravity("faces").setFlags("region_relative").setCrop("crop").chain()
  .setEffect("saturation:50").chain()
  .setEffect("vignette").chain()
  .setFlags("layer_apply").setWidth(100).setRadius("max").setGravity("center").setY(20).setX(-20).setCrop("scale")).generate("coffee_cup.jpg")!, cloudinary: cloudinary)
Image overlay with further manipulation

Learn more about Cloudinary’s image manipulation capabilities

Manipulating multiple image overlays

In addition to being able to manipulate a single image overlay, Cloudinary allows you to add and manipulate multiple overlays, as well. You can do this by chaining another overlay, setting the flags parameter to layer_apply (or fl_layer_apply for URLs), and applying multiple image transformations. Adding another overlay is as simple as manipulating a picture to suit the existing underlying and overlaying images. In our coffee mug example, we added a balloon as an additional overlay and performed the following manipulations: resized to be 30 pixels wide, changed the hue level to pink, and rotated it five degrees.

Ruby:
Copy to clipboard
cl_image_tag("coffee_cup.jpg", :transformation=>[
  {:width=>400, :height=>250, :gravity=>"south", :crop=>"fill"},
  {:overlay=>"nice_couple", :width=>1.3, :height=>1.3, :gravity=>"faces", :flags=>"region_relative", :crop=>"crop"},
  {:effect=>"saturation:50"},
  {:effect=>"vignette"},
  {:flags=>"layer_apply", :width=>100, :radius=>"max", :gravity=>"center", :y=>20, :x=>-20, :crop=>"scale"},
  {:overlay=>"balloon", :width=>30},
  {:effect=>"hue:-20", :angle=>5},
  {:flags=>"layer_apply", :x=>30, :y=>5}
  ])
PHP v1:
Copy to clipboard
cl_image_tag("coffee_cup.jpg", array("transformation"=>array(
  array("width"=>400, "height"=>250, "gravity"=>"south", "crop"=>"fill"),
  array("overlay"=>"nice_couple", "width"=>"1.3", "height"=>"1.3", "gravity"=>"faces", "flags"=>"region_relative", "crop"=>"crop"),
  array("effect"=>"saturation:50"),
  array("effect"=>"vignette"),
  array("flags"=>"layer_apply", "width"=>100, "radius"=>"max", "gravity"=>"center", "y"=>20, "x"=>-20, "crop"=>"scale"),
  array("overlay"=>"balloon", "width"=>30),
  array("effect"=>"hue:-20", "angle"=>5),
  array("flags"=>"layer_apply", "x"=>30, "y"=>5)
  )))
PHP v2:
Copy to clipboard
(new ImageTag('coffee_cup.jpg'))
  ->resize(Resize::fill()->width(400)->height(250)->gravity(Gravity::compass(Compass::south())))
  ->overlay(
      Overlay::source(Source::image('nice_couple')
        ->transformation((new ImageTransformation())
          ->resize(Resize::crop()->width(1.3)->height(1.3)
            ->gravity(Gravity::focusOn(FocusOn::faces()))
            ->regionRelative())
          ->adjust(Adjust::saturation()->level(50))
          ->effect(Effect::vignette())
          ->resize(Resize::scale()->width(100))
          ->roundCorners(RoundCorners::max())))
      ->position((new Position())
        ->gravity(Gravity::compass(Compass::center()))
        ->offsetX(-20)->offsetY(20)))
    ->overlay(
        Overlay::source(Source::image('balloon')
          ->transformation((new ImageTransformation())
            ->resize(Resize::scale()->width(30))
            ->rotate(Rotate::byAngle(5))
            ->adjust(Adjust::hue()->level(-20))))
        ->position((new Position())
          ->offsetX(30)->offsetY(5)
      
    ));
Python:
Copy to clipboard
CloudinaryImage("coffee_cup.jpg").image(transformation=[
  {'width': 400, 'height': 250, 'gravity': "south", 'crop': "fill"},
  {'overlay': "nice_couple", 'width': "1.3", 'height': "1.3", 'gravity': "faces", 'flags': "region_relative", 'crop': "crop"},
  {'effect': "saturation:50"},
  {'effect': "vignette"},
  {'flags': "layer_apply", 'width': 100, 'radius': "max", 'gravity': "center", 'y': 20, 'x': -20, 'crop': "scale"},
  {'overlay': "balloon", 'width': 30},
  {'effect': "hue:-20", 'angle': 5},
  {'flags': "layer_apply", 'x': 30, 'y': 5}
  ])
Node.js:
Copy to clipboard
cloudinary.image("coffee_cup.jpg", {transformation: [
  {width: 400, height: 250, gravity: "south", crop: "fill"},
  {overlay: "nice_couple", width: "1.3", height: "1.3", gravity: "faces", flags: "region_relative", crop: "crop"},
  {effect: "saturation:50"},
  {effect: "vignette"},
  {flags: "layer_apply", width: 100, radius: "max", gravity: "center", y: 20, x: -20, crop: "scale"},
  {overlay: "balloon", width: 30},
  {effect: "hue:-20", angle: 5},
  {flags: "layer_apply", x: 30, y: 5}
  ]})
Java:
Copy to clipboard
cloudinary.url().transformation(new Transformation()
  .width(400).height(250).gravity("south").crop("fill").chain()
  .overlay(new Layer().publicId("nice_couple")).width(1.3).height(1.3).gravity("faces").flags("region_relative").crop("crop").chain()
  .effect("saturation:50").chain()
  .effect("vignette").chain()
  .flags("layer_apply").width(100).radius("max").gravity("center").y(20).x(-20).crop("scale").chain()
  .overlay(new Layer().publicId("balloon")).width(30).chain()
  .effect("hue:-20").angle(5).chain()
  .flags("layer_apply").x(30).y(5)).imageTag("coffee_cup.jpg");
JS:
Copy to clipboard
cloudinary.imageTag('coffee_cup.jpg', {transformation: [
  {width: 400, height: 250, gravity: "south", crop: "fill"},
  {overlay: new cloudinary.Layer().publicId("nice_couple"), width: "1.3", height: "1.3", gravity: "faces", flags: "region_relative", crop: "crop"},
  {effect: "saturation:50"},
  {effect: "vignette"},
  {flags: "layer_apply", width: 100, radius: "max", gravity: "center", y: 20, x: -20, crop: "scale"},
  {overlay: new cloudinary.Layer().publicId("balloon"), width: 30},
  {effect: "hue:-20", angle: 5},
  {flags: "layer_apply", x: 30, y: 5}
  ]}).toHtml();
jQuery:
Copy to clipboard
$.cloudinary.image("coffee_cup.jpg", {transformation: [
  {width: 400, height: 250, gravity: "south", crop: "fill"},
  {overlay: new cloudinary.Layer().publicId("nice_couple"), width: "1.3", height: "1.3", gravity: "faces", flags: "region_relative", crop: "crop"},
  {effect: "saturation:50"},
  {effect: "vignette"},
  {flags: "layer_apply", width: 100, radius: "max", gravity: "center", y: 20, x: -20, crop: "scale"},
  {overlay: new cloudinary.Layer().publicId("balloon"), width: 30},
  {effect: "hue:-20", angle: 5},
  {flags: "layer_apply", x: 30, y: 5}
  ]})
React:
Copy to clipboard
<Image publicId="coffee_cup.jpg" >
  <Transformation width="400" height="250" gravity="south" crop="fill" />
  <Transformation overlay="nice_couple" width="1.3" height="1.3" gravity="faces" flags="region_relative" crop="crop" />
  <Transformation effect="saturation:50" />
  <Transformation effect="vignette" />
  <Transformation flags="layer_apply" width="100" radius="max" gravity="center" y="20" x="-20" crop="scale" />
  <Transformation overlay="balloon" width="30" />
  <Transformation effect="hue:-20" angle="5" />
  <Transformation flags="layer_apply" x="30" y="5" />
</Image>
Vue.js:
Copy to clipboard
<cld-image publicId="coffee_cup.jpg" >
  <cld-transformation width="400" height="250" gravity="south" crop="fill" />
  <cld-transformation :overlay="nice_couple" width="1.3" height="1.3" gravity="faces" flags="region_relative" crop="crop" />
  <cld-transformation effect="saturation:50" />
  <cld-transformation effect="vignette" />
  <cld-transformation flags="layer_apply" width="100" radius="max" gravity="center" y="20" x="-20" crop="scale" />
  <cld-transformation :overlay="balloon" width="30" />
  <cld-transformation effect="hue:-20" angle="5" />
  <cld-transformation flags="layer_apply" x="30" y="5" />
</cld-image>
Angular:
Copy to clipboard
<cl-image public-id="coffee_cup.jpg" >
  <cl-transformation width="400" height="250" gravity="south" crop="fill">
  </cl-transformation>
  <cl-transformation overlay="nice_couple" width="1.3" height="1.3" gravity="faces" flags="region_relative" crop="crop">
  </cl-transformation>
  <cl-transformation effect="saturation:50">
  </cl-transformation>
  <cl-transformation effect="vignette">
  </cl-transformation>
  <cl-transformation flags="layer_apply" width="100" radius="max" gravity="center" y="20" x="-20" crop="scale">
  </cl-transformation>
  <cl-transformation overlay="balloon" width="30">
  </cl-transformation>
  <cl-transformation effect="hue:-20" angle="5">
  </cl-transformation>
  <cl-transformation flags="layer_apply" x="30" y="5">
  </cl-transformation>
</cl-image>
.NET:
Copy to clipboard
cloudinary.Api.UrlImgUp.Transform(new Transformation()
  .Width(400).Height(250).Gravity("south").Crop("fill").Chain()
  .Overlay(new Layer().PublicId("nice_couple")).Width(1.3).Height(1.3).Gravity("faces").Flags("region_relative").Crop("crop").Chain()
  .Effect("saturation:50").Chain()
  .Effect("vignette").Chain()
  .Flags("layer_apply").Width(100).Radius("max").Gravity("center").Y(20).X(-20).Crop("scale").Chain()
  .Overlay(new Layer().PublicId("balloon")).Width(30).Chain()
  .Effect("hue:-20").Angle(5).Chain()
  .Flags("layer_apply").X(30).Y(5)).BuildImageTag("coffee_cup.jpg")
Android:
Copy to clipboard
MediaManager.get().url().transformation(new Transformation()
  .width(400).height(250).gravity("south").crop("fill").chain()
  .overlay(new Layer().publicId("nice_couple")).width(1.3).height(1.3).gravity("faces").flags("region_relative").crop("crop").chain()
  .effect("saturation:50").chain()
  .effect("vignette").chain()
  .flags("layer_apply").width(100).radius("max").gravity("center").y(20).x(-20).crop("scale").chain()
  .overlay(new Layer().publicId("balloon")).width(30).chain()
  .effect("hue:-20").angle(5).chain()
  .flags("layer_apply").x(30).y(5)).generate("coffee_cup.jpg");
iOS:
Copy to clipboard
imageView.cldSetImage(cloudinary.createUrl().setTransformation(CLDTransformation()
  .setWidth(400).setHeight(250).setGravity("south").setCrop("fill").chain()
  .setOverlay("nice_couple").setWidth(1.3).setHeight(1.3).setGravity("faces").setFlags("region_relative").setCrop("crop").chain()
  .setEffect("saturation:50").chain()
  .setEffect("vignette").chain()
  .setFlags("layer_apply").setWidth(100).setRadius("max").setGravity("center").setY(20).setX(-20).setCrop("scale").chain()
  .setOverlay("balloon").setWidth(30).chain()
  .setEffect("hue:-20").setAngle(5).chain()
  .setFlags("layer_apply").setX(30).setY(5)).generate("coffee_cup.jpg")!, cloudinary: cloudinary)
Multiple image overlays with manipulations

Manipulating text overlays

Cloudinary supports adding dynamic text overlays in any style of customized text. What's more, the text overlay can be further manipulated, just like image overlays. So, returning to our example, we have now added a text overlay that we have colored, using the colorize effect, and rotated.

Ruby:
Copy to clipboard
cl_image_tag("coffee_cup.jpg", :transformation=>[
  {:width=>400, :height=>250, :gravity=>"south", :crop=>"fill"},
  {:overlay=>"nice_couple", :width=>1.3, :height=>1.3, :gravity=>"faces", :flags=>"region_relative", :crop=>"crop"},
  {:effect=>"saturation:50"},
  {:effect=>"vignette"},
  {:flags=>"layer_apply", :width=>100, :radius=>"max", :gravity=>"center", :y=>20, :x=>-20, :crop=>"scale"},
  {:overlay=>"balloon", :width=>30},
  {:effect=>"hue:-20", :angle=>5},
  {:flags=>"layer_apply", :x=>30, :y=>5},
  {:overlay=>{:font_family=>"Cookie", :font_size=>40, :font_weight=>"bold", :text=>"Love"}, :effect=>"colorize", :color=>"#f08"},
  {:angle=>20, :flags=>"layer_apply", :x=>-45, :y=>44}
  ])
PHP v1:
Copy to clipboard
cl_image_tag("coffee_cup.jpg", array("transformation"=>array(
  array("width"=>400, "height"=>250, "gravity"=>"south", "crop"=>"fill"),
  array("overlay"=>"nice_couple", "width"=>"1.3", "height"=>"1.3", "gravity"=>"faces", "flags"=>"region_relative", "crop"=>"crop"),
  array("effect"=>"saturation:50"),
  array("effect"=>"vignette"),
  array("flags"=>"layer_apply", "width"=>100, "radius"=>"max", "gravity"=>"center", "y"=>20, "x"=>-20, "crop"=>"scale"),
  array("overlay"=>"balloon", "width"=>30),
  array("effect"=>"hue:-20", "angle"=>5),
  array("flags"=>"layer_apply", "x"=>30, "y"=>5),
  array("overlay"=>array("font_family"=>"Cookie", "font_size"=>40, "font_weight"=>"bold", "text"=>"Love"), "effect"=>"colorize", "color"=>"#f08"),
  array("angle"=>20, "flags"=>"layer_apply", "x"=>-45, "y"=>44)
  )))
PHP v2:
Copy to clipboard
(new ImageTag('coffee_cup.jpg'))
  ->resize(Resize::fill()->width(400)->height(250)->gravity(Gravity::compass(Compass::south())))
  ->overlay(
      Overlay::source(Source::image('nice_couple')
        ->transformation((new ImageTransformation())
          ->resize(Resize::crop()->width(1.3)->height(1.3)
            ->gravity(Gravity::focusOn(FocusOn::faces()))
            ->regionRelative())
          ->adjust(Adjust::saturation()->level(50))
          ->effect(Effect::vignette())
          ->resize(Resize::scale()->width(100))
          ->roundCorners(RoundCorners::max())))
      ->position((new Position())
        ->gravity(Gravity::compass(Compass::center()))
        ->offsetX(-20)->offsetY(20)))
    ->overlay(
        Overlay::source(Source::image('balloon')
          ->transformation((new ImageTransformation())
            ->resize(Resize::scale()->width(30))
            ->rotate(Rotate::byAngle(5))
            ->adjust(Adjust::hue()->level(-20))))
        ->position((new Position())
          ->offsetX(30)->offsetY(5)))
          ->overlay(
              Overlay::source(
                  Source::text('Love', (new TextStyle('Cookie', 40))
                    ->fontWeight(FontWeight::bold()))
                  ->textColor(Color::rgb('f08'))
                  ->transformation((new ImageTransformation())
                    ->effect(Effect::colorize())
                    ->rotate(Rotate::byAngle(20))))
                ->position((new Position())
                  ->offsetX(-45)->offsetY(44)
              
            
              
              ));
Python:
Copy to clipboard
CloudinaryImage("coffee_cup.jpg").image(transformation=[
  {'width': 400, 'height': 250, 'gravity': "south", 'crop': "fill"},
  {'overlay': "nice_couple", 'width': "1.3", 'height': "1.3", 'gravity': "faces", 'flags': "region_relative", 'crop': "crop"},
  {'effect': "saturation:50"},
  {'effect': "vignette"},
  {'flags': "layer_apply", 'width': 100, 'radius': "max", 'gravity': "center", 'y': 20, 'x': -20, 'crop': "scale"},
  {'overlay': "balloon", 'width': 30},
  {'effect': "hue:-20", 'angle': 5},
  {'flags': "layer_apply", 'x': 30, 'y': 5},
  {'overlay': {'font_family': "Cookie", 'font_size': 40, 'font_weight': "bold", 'text': "Love"}, 'effect': "colorize", 'color': "#f08"},
  {'angle': 20, 'flags': "layer_apply", 'x': -45, 'y': 44}
  ])
Node.js:
Copy to clipboard
cloudinary.image("coffee_cup.jpg", {transformation: [
  {width: 400, height: 250, gravity: "south", crop: "fill"},
  {overlay: "nice_couple", width: "1.3", height: "1.3", gravity: "faces", flags: "region_relative", crop: "crop"},
  {effect: "saturation:50"},
  {effect: "vignette"},
  {flags: "layer_apply", width: 100, radius: "max", gravity: "center", y: 20, x: -20, crop: "scale"},
  {overlay: "balloon", width: 30},
  {effect: "hue:-20", angle: 5},
  {flags: "layer_apply", x: 30, y: 5},
  {overlay: {font_family: "Cookie", font_size: 40, font_weight: "bold", text: "Love"}, effect: "colorize", color: "#f08"},
  {angle: 20, flags: "layer_apply", x: -45, y: 44}
  ]})
Java:
Copy to clipboard
cloudinary.url().transformation(new Transformation()
  .width(400).height(250).gravity("south").crop("fill").chain()
  .overlay(new Layer().publicId("nice_couple")).width(1.3).height(1.3).gravity("faces").flags("region_relative").crop("crop").chain()
  .effect("saturation:50").chain()
  .effect("vignette").chain()
  .flags("layer_apply").width(100).radius("max").gravity("center").y(20).x(-20).crop("scale").chain()
  .overlay(new Layer().publicId("balloon")).width(30).chain()
  .effect("hue:-20").angle(5).chain()
  .flags("layer_apply").x(30).y(5).chain()
  .overlay(new TextLayer().fontFamily("Cookie").fontSize(40).fontWeight("bold").text("Love")).effect("colorize").color("#f08").chain()
  .angle(20).flags("layer_apply").x(-45).y(44)).imageTag("coffee_cup.jpg");
JS:
Copy to clipboard
cloudinary.imageTag('coffee_cup.jpg', {transformation: [
  {width: 400, height: 250, gravity: "south", crop: "fill"},
  {overlay: new cloudinary.Layer().publicId("nice_couple"), width: "1.3", height: "1.3", gravity: "faces", flags: "region_relative", crop: "crop"},
  {effect: "saturation:50"},
  {effect: "vignette"},
  {flags: "layer_apply", width: 100, radius: "max", gravity: "center", y: 20, x: -20, crop: "scale"},
  {overlay: new cloudinary.Layer().publicId("balloon"), width: 30},
  {effect: "hue:-20", angle: 5},
  {flags: "layer_apply", x: 30, y: 5},
  {overlay: new cloudinary.TextLayer().fontFamily("Cookie").fontSize(40).fontWeight("bold").text("Love"), effect: "colorize", color: "#f08"},
  {angle: 20, flags: "layer_apply", x: -45, y: 44}
  ]}).toHtml();
jQuery:
Copy to clipboard
$.cloudinary.image("coffee_cup.jpg", {transformation: [
  {width: 400, height: 250, gravity: "south", crop: "fill"},
  {overlay: new cloudinary.Layer().publicId("nice_couple"), width: "1.3", height: "1.3", gravity: "faces", flags: "region_relative", crop: "crop"},
  {effect: "saturation:50"},
  {effect: "vignette"},
  {flags: "layer_apply", width: 100, radius: "max", gravity: "center", y: 20, x: -20, crop: "scale"},
  {overlay: new cloudinary.Layer().publicId("balloon"), width: 30},
  {effect: "hue:-20", angle: 5},
  {flags: "layer_apply", x: 30, y: 5},
  {overlay: new cloudinary.TextLayer().fontFamily("Cookie").fontSize(40).fontWeight("bold").text("Love"), effect: "colorize", color: "#f08"},
  {angle: 20, flags: "layer_apply", x: -45, y: 44}
  ]})
React:
Copy to clipboard
<Image publicId="coffee_cup.jpg" >
  <Transformation width="400" height="250" gravity="south" crop="fill" />
  <Transformation overlay="nice_couple" width="1.3" height="1.3" gravity="faces" flags="region_relative" crop="crop" />
  <Transformation effect="saturation:50" />
  <Transformation effect="vignette" />
  <Transformation flags="layer_apply" width="100" radius="max" gravity="center" y="20" x="-20" crop="scale" />
  <Transformation overlay="balloon" width="30" />
  <Transformation effect="hue:-20" angle="5" />
  <Transformation flags="layer_apply" x="30" y="5" />
  <Transformation overlay={{fontFamily: "Cookie", fontSize: 40, fontWeight: "bold", text: "Love"}} effect="colorize" color="#f08" />
  <Transformation angle="20" flags="layer_apply" x="-45" y="44" />
</Image>
Vue.js:
Copy to clipboard
<cld-image publicId="coffee_cup.jpg" >
  <cld-transformation width="400" height="250" gravity="south" crop="fill" />
  <cld-transformation :overlay="nice_couple" width="1.3" height="1.3" gravity="faces" flags="region_relative" crop="crop" />
  <cld-transformation effect="saturation:50" />
  <cld-transformation effect="vignette" />
  <cld-transformation flags="layer_apply" width="100" radius="max" gravity="center" y="20" x="-20" crop="scale" />
  <cld-transformation :overlay="balloon" width="30" />
  <cld-transformation effect="hue:-20" angle="5" />
  <cld-transformation flags="layer_apply" x="30" y="5" />
  <cld-transformation :overlay="{fontFamily: 'Cookie', fontSize: 40, fontWeight: 'bold', text: 'Love'}" effect="colorize" color="#f08" />
  <cld-transformation angle="20" flags="layer_apply" x="-45" y="44" />
</cld-image>
Angular:
Copy to clipboard
<cl-image public-id="coffee_cup.jpg" >
  <cl-transformation width="400" height="250" gravity="south" crop="fill">
  </cl-transformation>
  <cl-transformation overlay="nice_couple" width="1.3" height="1.3" gravity="faces" flags="region_relative" crop="crop">
  </cl-transformation>
  <cl-transformation effect="saturation:50">
  </cl-transformation>
  <cl-transformation effect="vignette">
  </cl-transformation>
  <cl-transformation flags="layer_apply" width="100" radius="max" gravity="center" y="20" x="-20" crop="scale">
  </cl-transformation>
  <cl-transformation overlay="balloon" width="30">
  </cl-transformation>
  <cl-transformation effect="hue:-20" angle="5">
  </cl-transformation>
  <cl-transformation flags="layer_apply" x="30" y="5">
  </cl-transformation>
  <cl-transformation overlay="text:Cookie_40_bold:Love" effect="colorize" color="#f08">
  </cl-transformation>
  <cl-transformation angle="20" flags="layer_apply" x="-45" y="44">
  </cl-transformation>
</cl-image>
.NET:
Copy to clipboard
cloudinary.Api.UrlImgUp.Transform(new Transformation()
  .Width(400).Height(250).Gravity("south").Crop("fill").Chain()
  .Overlay(new Layer().PublicId("nice_couple")).Width(1.3).Height(1.3).Gravity("faces").Flags("region_relative").Crop("crop").Chain()
  .Effect("saturation:50").Chain()
  .Effect("vignette").Chain()
  .Flags("layer_apply").Width(100).Radius("max").Gravity("center").Y(20).X(-20).Crop("scale").Chain()
  .Overlay(new Layer().PublicId("balloon")).Width(30).Chain()
  .Effect("hue:-20").Angle(5).Chain()
  .Flags("layer_apply").X(30).Y(5).Chain()
  .Overlay(new TextLayer().FontFamily("Cookie").FontSize(40).FontWeight("bold").Text("Love")).Effect("colorize").Color("#f08").Chain()
  .Angle(20).Flags("layer_apply").X(-45).Y(44)).BuildImageTag("coffee_cup.jpg")
Android:
Copy to clipboard
MediaManager.get().url().transformation(new Transformation()
  .width(400).height(250).gravity("south").crop("fill").chain()
  .overlay(new Layer().publicId("nice_couple")).width(1.3).height(1.3).gravity("faces").flags("region_relative").crop("crop").chain()
  .effect("saturation:50").chain()
  .effect("vignette").chain()
  .flags("layer_apply").width(100).radius("max").gravity("center").y(20).x(-20).crop("scale").chain()
  .overlay(new Layer().publicId("balloon")).width(30).chain()
  .effect("hue:-20").angle(5).chain()
  .flags("layer_apply").x(30).y(5).chain()
  .overlay(new TextLayer().fontFamily("Cookie").fontSize(40).fontWeight("bold").text("Love")).effect("colorize").color("#f08").chain()
  .angle(20).flags("layer_apply").x(-45).y(44)).generate("coffee_cup.jpg");
iOS:
Copy to clipboard
imageView.cldSetImage(cloudinary.createUrl().setTransformation(CLDTransformation()
  .setWidth(400).setHeight(250).setGravity("south").setCrop("fill").chain()
  .setOverlay("nice_couple").setWidth(1.3).setHeight(1.3).setGravity("faces").setFlags("region_relative").setCrop("crop").chain()
  .setEffect("saturation:50").chain()
  .setEffect("vignette").chain()
  .setFlags("layer_apply").setWidth(100).setRadius("max").setGravity("center").setY(20).setX(-20).setCrop("scale").chain()
  .setOverlay("balloon").setWidth(30).chain()
  .setEffect("hue:-20").setAngle(5).chain()
  .setFlags("layer_apply").setX(30).setY(5).chain()
  .setOverlay("text:Cookie_40_bold:Love").setEffect("colorize").setColor("#f08").chain()
  .setAngle(20).setFlags("layer_apply").setX(-45).setY(44)).generate("coffee_cup.jpg")!, cloudinary: cloudinary)
Multiple image and text overlays with further manipulation

Summary

Cloudinary’s powerful capabilities allow you to manipulate and generate complex, combined images that match your graphic design requirements. You can use Cloudinary's dynamic manipulation URLs with user-uploaded images in order to manipulate the images while combining multiple images and text overlays into a single new image. With the new features introduced in this post, you can apply Cloudinary’s rich set of manipulation capabilities separately on each layer of underlying or overlaying images or text. All overlay manipulation features are available with all Cloudinary plans, including the free tier. If you don't have a Cloudinary account yet, sign up for a free account here.

Recent Blog Posts

Create Lightweight Sites With Low-Code and No-Code Technology

Consumers expect modern websites to be mainly visual. But, the more compelling and complex the related media is, the more data is involved, compounding the site’s weight. In today’s content-craving world, delivering unoptimized media can cost you because it leads to sluggish page loads, resulting in visitors abandoning your site in search of a faster alternative. In fact, a page load that takes more than three seconds can cause as many as 40% of your visitors to bounce. Given this competitive, digital-first environment, you can’t afford to lose page views, for time is of the essence.

Read more
A Blueprint for AWS-Secured Webhook Listeners for Cloudinary

tl;dr: An AWS-secured and optimized Cloudinary webhook listener for extending the Cloudinary service

Code: Github

A webhook is a communication medium for sending notifications from one platform to another about events that occurred. In place are user-defined HTTP callbacks that are triggered by specific events. When a triggered event takes place on the source site, the webhook listens to the event, collects the data, and sends it to the URL you specified in the form of an HTTP request.

Read more
New Accessibility Features for Cloudinary’s Product Gallery Widget

Cloudinary’s Product Gallery widget, which launched in 2019, has enabled many brands to effectively and efficiently showcase their products in a sleek and captivating manner, saving countless hours of development time and accelerating release cycles. By adding Cloudinary’s Product Gallery widget with its customizable UI to their product page, retailers reap numerous benefits, often turning visitors into customers in short order.

Read more
Why Successful Businesses Engage With and Convert Audiences With Visual Media

Most business buyers prefer to research purchase options online, as do many shoppers. No wonder online retail sales in the U.S. rose by 32.4% in 2020—an impressive gain of $105 billion.

For B2B and B2C businesses, text-heavy websites are no longer adequate in attracting shoppers. Instead, engaging visual media—spin images, videos, 3D models, augmented reality—are becoming a must for conveying eye-catching details and differentiators about products or services.

Read more
Making User-Generated Content (UGC) Shoppable With Cloudinary

User-generated content (UGC) is a powerful marketing tool. Not only does video complement marketing efforts for e-commerce by enabling customers to explore products in greater detail, but UGC also adds an element of trust. As a bonus, user-generated video is an exceptional opportunity for e-businesses to attract website traffic without their marketing team having to create promotional videos from scratch. User-generated content drives conversions and brand loyalty as a direct result of authentic interaction.

Read more