Cloudinary Blog

User-defined variables can transform the way you deliver media

Automating image manipulation with user-defined variables

No programmer could imagine a world without variables. Neither can Cloudinary. That’s why Cloudinary now offers image transformations that support user-defined variables.

Using Cloudinary, you can already deliver transformed images with a variety of transformations, including resizing, cropping, rotating, a huge toolbox of special effects and filters, text and image overlays, and more, all of which you can deliver responsively, on-the-fly, and optimized for quick delivery via CDN.

But there are many scenarios when you may want to set up a fixed set of transformations to be applied to a variety of media assets, with one or a few variations depending on the specific image to be delivered, or on other factors that are determined at a later time.

By including user-defined variables in your transformations, you can assign different values to specific elements of the transformation when you deliver the file. For example, you could:

  • Apply resizing, cropping, and certain special effect transformations to all images, but use a variable for the text overlay element of the transformation, so that each image can be customized with a user’s name.

  • Calculate the required width of an overlay based on the evaluation of a condition, and then pass that number as a variable value to the transformation component where the overlay is applied.

  • Set certain elements of your Web site profile images, such as size, circular shape, quality, and more, but allow your users to select from special effects and artistic filters to apply.

Until now, goals like these could only be accomplished through a combination of transformation API and your own application code. Now you can do it all within the transformation code.

Webinar
How to Optimize for Page Load Speed

User-defined variables can be useful in conjunction with conditional transformations. You can simplify and improve the legibility of conditional transformation code by assigning different values to variables based on conditions, and then pass the relevant value to the transformation code in a separate component of a chained transformation.

You can also now apply arithmetic operators to numeric parameters or user-defined variables representing numeric parameters. To make it more interesting, your user-defined variables can even take on the value of an already known parameter value adjusted by some arithmetic expression. For example, you could assign the textwidth variable to be equal to the width of the image minus 10. Then you could create a text overlay whose width is assigned the value of the textwidth variable.

User-defined variables are even more valuable when working with named transformations, which enable you to keep your transformation code completely separate from your URL delivery code, and now enable you to keep variable values separate from your transformation code as well.

Between variables, conditional transformations, arithmetic expressions, and the function-like named transformations, you've nearly got everything you need to consider the Cloudinary transformation API a programming language in and of itself.

The basics

In general, to include a user-defined variable in your transformation, you just specify any variable name of your choice preceded by the $ sign, instead of a static parameter value, and you assign values to your variable similar to the way you would for a regular parameter.

Numbers

This is all it takes to create a basic user-defined variable for a numeric parameter:

Ruby:
Copy to clipboard
cl_image_tag("apple_size.jpg", :width=>"$imgwidth", :crop=>"scale", :variables=>[["$imgwidth", "270"]])
PHP v1:
Copy to clipboard
cl_image_tag("apple_size.jpg", array("width"=>"$imgwidth", "crop"=>"scale", "variables"=>array("$imgwidth"=>"270")))
PHP v2:
Copy to clipboard
(new ImageTag('apple_size.jpg'))
  ->addVariable(Variable::set('imgwidth', 270))
  ->resize(Resize::scale()->width('$imgwidth'));
Python:
Copy to clipboard
CloudinaryImage("apple_size.jpg").image(width="$imgwidth", crop="scale", variables={"$imgwidth": "270"})
Node.js:
Copy to clipboard
cloudinary.image("apple_size.jpg", {width: "$imgwidth", crop: "scale", variables: [["$imgwidth", "270"]]})
Java:
Copy to clipboard
cloudinary.url().transformation(new Transformation()
.variables(variable("$imgwidth","270")).chain().width("$imgwidth").crop("scale")).imageTag("apple_size.jpg");
JS:
Copy to clipboard
cloudinary.imageTag('apple_size.jpg', {width: "$imgwidth", crop: "scale", variables: [["$imgwidth", "270"]]}).toHtml();
jQuery:
Copy to clipboard
$.cloudinary.image("apple_size.jpg", {width: "$imgwidth", crop: "scale", variables: [["$imgwidth", "270"]]})
React:
Copy to clipboard
<Image publicId="apple_size.jpg" variables={[["$imgwidth", "270"]]}>
  <Transformation width="$imgwidth" crop="scale" />
</Image>
Vue.js:
Copy to clipboard
<cld-image publicId="apple_size.jpg" :variables="[['$imgwidth', '270']]">
  <cld-transformation width="$imgwidth" crop="scale" />
</cld-image>
Angular:
Copy to clipboard
<cl-image public-id="apple_size.jpg" variables="[['$imgwidth', '270']]">
  <cl-transformation width="$imgwidth" crop="scale">
  </cl-transformation>
</cl-image>
.NET:
Copy to clipboard
cloudinary.Api.UrlImgUp.Transform(new Transformation().Width("$imgwidth").Crop("scale")).BuildImageTag("apple_size.jpg")
Android:
Copy to clipboard
MediaManager.get().url().transformation(new Transformation()
.variables(variable("$imgwidth","270")).chain().width("$imgwidth").crop("scale")).generate("apple_size.jpg");
iOS:
Copy to clipboard
imageView.cldSetImage(cloudinary.createUrl().setTransformation(CLDTransformation().setWidth("$imgwidth").setCrop("scale")).generate("apple_size.jpg")!, cloudinary: cloudinary)
simple variable

As you can see in this simplified example, we created a user-defined variable named imgwidth, initialized it with the value 270, and then later assigned the imgwidth variable to the width parameter.

Strings

You use variables for string parameters in a similar way, but you bound the string value with ! ! characters in the assignment, for example, !MyStringValue!.

For example, have a look at this basket of strings. There are several transformations, but the value of the effect gets a different string value in each case, controlled by a variable:

variable value = red variable value = red variable value = red

Below, we show how to deliver the sepia image on the right: we assign the !sepia! string to the $effect variable, and then we set the effect transformation parameter to use whatever value is in the $effect parameter:

Ruby:
Copy to clipboard
cl_image_tag("strings.jpg", :variables=>[["$effect", "!sepia!"]], :transformation=>[
  {:width=>1000, :crop=>"scale"},
  {:effect=>"$effect"},
  {:border=>"20px_solid_white"}
  ])
PHP v1:
Copy to clipboard
cl_image_tag("strings.jpg", array("variables"=>array("$effect"=>"!sepia!"), "transformation"=>array(
  array("width"=>1000, "crop"=>"scale"),
  array("effect"=>"$effect"),
  array("border"=>"20px_solid_white")
  )))
PHP v2:
Copy to clipboard
This code example is not currently available.
Python:
Copy to clipboard
CloudinaryImage("strings.jpg").image(variables={"$effect": "!sepia!"}, transformation=[
  {'width': 1000, 'crop': "scale"},
  {'effect': "$effect"},
  {'border': "20px_solid_white"}
  ])
Node.js:
Copy to clipboard
cloudinary.image("strings.jpg", {variables: [["$effect", "!sepia!"]], transformation: [
  {width: 1000, crop: "scale"},
  {effect: "$effect"},
  {border: "20px_solid_white"}
  ]})
Java:
Copy to clipboard
cloudinary.url().transformation(new Transformation()
.variables(variable("$effect","!sepia!")).chain()
  .width(1000).crop("scale").chain()
  .effect("$effect").chain()
  .border("20px_solid_white")).imageTag("strings.jpg");
JS:
Copy to clipboard
cloudinary.imageTag('strings.jpg', {variables: [["$effect", "!sepia!"]], transformation: [
  {width: 1000, crop: "scale"},
  {effect: "$effect"},
  {border: "20px_solid_white"}
  ]}).toHtml();
jQuery:
Copy to clipboard
$.cloudinary.image("strings.jpg", {variables: [["$effect", "!sepia!"]], transformation: [
  {width: 1000, crop: "scale"},
  {effect: "$effect"},
  {border: "20px_solid_white"}
  ]})
React:
Copy to clipboard
<Image publicId="strings.jpg" variables={[["$effect", "!sepia!"]]}>
  <Transformation width="1000" crop="scale" />
  <Transformation effect="$effect" />
  <Transformation border="20px_solid_white" />
</Image>
Vue.js:
Copy to clipboard
<cld-image publicId="strings.jpg" :variables="[['$effect', '!sepia!']]">
  <cld-transformation width="1000" crop="scale" />
  <cld-transformation effect="$effect" />
  <cld-transformation border="20px_solid_white" />
</cld-image>
Angular:
Copy to clipboard
<cl-image public-id="strings.jpg" variables="[['$effect', '!sepia!']]">
  <cl-transformation width="1000" crop="scale">
  </cl-transformation>
  <cl-transformation effect="$effect">
  </cl-transformation>
  <cl-transformation border="20px_solid_white">
  </cl-transformation>
</cl-image>
.NET:
Copy to clipboard
cloudinary.Api.UrlImgUp.Transform(new Transformation()
  .Width(1000).Crop("scale").Chain()
  .Effect("$effect").Chain()
  .Border("20px_solid_white")).BuildImageTag("strings.jpg")
Android:
Copy to clipboard
MediaManager.get().url().transformation(new Transformation()
.variables(variable("$effect","!sepia!")).chain()
  .width(1000).crop("scale").chain()
  .effect("$effect").chain()
  .border("20px_solid_white")).generate("strings.jpg");
iOS:
Copy to clipboard
imageView.cldSetImage(cloudinary.createUrl().setTransformation(CLDTransformation()
  .setWidth(1000).setCrop("scale").chain()
  .setEffect("$effect").chain()
  .setBorder("20px_solid_white")).generate("strings.jpg")!, cloudinary: cloudinary)

Text overlays

You can also use string variables for the text, or even part of the text, in a text overlay. To differentiate the variable from the rest of the text in the text overlay, there's a special variable syntax: $(variablename). Below, the text overlay has fixed text © followed by the variable name, using the syntax $(name).

Ruby:
Copy to clipboard
cl_image_tag("canyons.jpg", :variables=>[["$name", "!Jane Smith!"]], :transformation=>[
  {:width=>1080, :crop=>"scale"},
  {:overlay=>{:font_family=>"MS", :font_size=>36, :text=>"%20%C2%A9%24%28name%29%20"}, :gravity=>"south_east", :x=>10, :y=>10, :background=>"#e7e5e5", :opacity=>75, :radius=>15}
  ])
PHP v1:
Copy to clipboard
cl_image_tag("canyons.jpg", array("variables"=>array("$name"=>"!Jane Smith!"), "transformation"=>array(
  array("width"=>1080, "crop"=>"scale"),
  array("overlay"=>array("font_family"=>"MS", "font_size"=>36, "text"=>"%20%C2%A9%24%28name%29%20"), "gravity"=>"south_east", "x"=>10, "y"=>10, "background"=>"#e7e5e5", "opacity"=>75, "radius"=>15)
  )))
PHP v2:
Copy to clipboard
(new ImageTag('canyons.jpg'))
  ->addVariable(Variable::set('name', 'Jane Smith'))
  ->resize(Resize::scale()->width(1080))
  ->overlay(
      Overlay::source(Source::text(' ©$(name) ', (new TextStyle('Comic Sans MS', 36)))
        ->backgroundColor(Color::rgb('e7e5e5'))
        ->transformation((new ImageTransformation())
          ->roundCorners(RoundCorners::byRadius(15))
          ->adjust(Adjust::opacity(75))))
      ->position((new Position())
        ->gravity(Gravity::compass(Compass::southEast()))
        ->offsetX(10)->offsetY(10)
  ));
Python:
Copy to clipboard
CloudinaryImage("canyons.jpg").image(variables={"$name": "!Jane Smith!"}, transformation=[
  {'width': 1080, 'crop': "scale"},
  {'overlay': {'font_family': "MS", 'font_size': 36, 'text': "%20%C2%A9%24%28name%29%20"}, 'gravity': "south_east", 'x': 10, 'y': 10, 'background': "#e7e5e5", 'opacity': 75, 'radius': 15}
  ])
Node.js:
Copy to clipboard
cloudinary.image("canyons.jpg", {variables: [["$name", "!Jane Smith!"]], transformation: [
  {width: 1080, crop: "scale"},
  {overlay: {font_family: "MS", font_size: 36, text: "%20%C2%A9%24%28name%29%20"}, gravity: "south_east", x: 10, y: 10, background: "#e7e5e5", opacity: 75, radius: 15}
  ]})
Java:
Copy to clipboard
cloudinary.url().transformation(new Transformation()
.variables(variable("$name","!Jane Smith!")).chain()
  .width(1080).crop("scale").chain()
  .overlay(new TextLayer().fontFamily("MS").fontSize(36).text("%20%C2%A9%24%28name%29%20")).gravity("south_east").x(10).y(10).background("#e7e5e5").opacity(75).radius(15)).imageTag("canyons.jpg");
JS:
Copy to clipboard
cloudinary.imageTag('canyons.jpg', {variables: [["$name", "!Jane Smith!"]], transformation: [
  {width: 1080, crop: "scale"},
  {overlay: new cloudinary.TextLayer().fontFamily("MS").fontSize(36).text("%20%C2%A9%24%28name%29%20"), gravity: "south_east", x: 10, y: 10, background: "#e7e5e5", opacity: 75, radius: 15}
  ]}).toHtml();
jQuery:
Copy to clipboard
$.cloudinary.image("canyons.jpg", {variables: [["$name", "!Jane Smith!"]], transformation: [
  {width: 1080, crop: "scale"},
  {overlay: new cloudinary.TextLayer().fontFamily("MS").fontSize(36).text("%20%C2%A9%24%28name%29%20"), gravity: "south_east", x: 10, y: 10, background: "#e7e5e5", opacity: 75, radius: 15}
  ]})
React:
Copy to clipboard
<Image publicId="canyons.jpg" variables={[["$name", "!Jane Smith!"]]}>
  <Transformation width="1080" crop="scale" />
  <Transformation overlay={{fontFamily: "MS", fontSize: 36, text: "%20%C2%A9%24%28name%29%20"}} gravity="south_east" x="10" y="10" background="#e7e5e5" opacity="75" radius="15" />
</Image>
Vue.js:
Copy to clipboard
<cld-image publicId="canyons.jpg" :variables="[['$name', '!Jane Smith!']]">
  <cld-transformation width="1080" crop="scale" />
  <cld-transformation :overlay="{fontFamily: 'MS', fontSize: 36, text: '%20%C2%A9%24%28name%29%20'}" gravity="south_east" x="10" y="10" background="#e7e5e5" opacity="75" radius="15" />
</cld-image>
Angular:
Copy to clipboard
<cl-image public-id="canyons.jpg" variables="[['$name', '!Jane Smith!']]">
  <cl-transformation width="1080" crop="scale">
  </cl-transformation>
  <cl-transformation overlay="text:Comic%20Sans%20MS_36:%20%C2%A9%24%28name%29%20" gravity="south_east" x="10" y="10" background="#e7e5e5" opacity="75" radius="15">
  </cl-transformation>
</cl-image>
.NET:
Copy to clipboard
cloudinary.Api.UrlImgUp.Transform(new Transformation()
  .Width(1080).Crop("scale").Chain()
  .Overlay(new TextLayer().FontFamily("MS").FontSize(36).Text("%20%C2%A9%24%28name%29%20")).Gravity("south_east").X(10).Y(10).Background("#e7e5e5").Opacity(75).Radius(15)).BuildImageTag("canyons.jpg")
Android:
Copy to clipboard
MediaManager.get().url().transformation(new Transformation()
.variables(variable("$name","!Jane Smith!")).chain()
  .width(1080).crop("scale").chain()
  .overlay(new TextLayer().fontFamily("MS").fontSize(36).text("%20%C2%A9%24%28name%29%20")).gravity("south_east").x(10).y(10).background("#e7e5e5").opacity(75).radius(15)).generate("canyons.jpg");
iOS:
Copy to clipboard
imageView.cldSetImage(cloudinary.createUrl().setTransformation(CLDTransformation()
  .setWidth(1080).setCrop("scale").chain()
  .setOverlay("text:Comic%20Sans%20MS_36:%20%C2%A9%24%28name%29%20").setGravity("south_east").setX(10).setY(10).setBackground("#e7e5e5").setOpacity(75).setRadius(15)).generate("canyons.jpg")!, cloudinary: cloudinary)
Photo with photographer name as text overlay

Note: You can substitute user-defined variables for the value of all single-value numeric transformation parameters and some of the string transformation parameters. (See the documentation for the exact list.)

Achieving full separation

To simplify the examples above, we demonstrated assigning variable values and using the variables within transformations, together in the same delivery URL.

However, in most real-life scenarios, you will probably use variable transformations within a named transformation. A named transformation is kind of like a transformation function you can 'call' from any transformation URL. But until now, you couldn't pass any external values to the named transformation. Now with user-defined variables you can.

User-defined variables thus enable a complete separation of the transformation from the values used for delivery; a separation of the design and content logic from the technical logic.

This separation takes named transformations to a whole new level, making it significantly easier to reuse common transformations for many images, even when some specific adjustments must be made to the transformation depending on the specific image, the location where the image is displayed, or other dependencies.

For example, we've saved a transformation similar to the one used in the canyon photo above, in a named transformation called signed_photos. The named transformation sets the font type and size, the location of the text overlay, parameters that define the semi-transparent background for the text, and also includes variables for the photographer's name and for the final width of the photo to deliver:

signed_photos transformation: c_scale,w_1080/b_rgb:e7e5e5,g_south_east,l_text:Comic Sans MS_30: © $(name)%20,o_60,r_15,x_10,y_10/c_scale,w_$finalwidth

Using that named transformation, you could then deliver any image by assigning the relevant photographer name and the final width you want for the resulting image, and then specifying the named transformation and any public ID:

Ruby:
Copy to clipboard
cl_image_tag("greece_seaport.jpg", :transformation=>["signed_photos"], :variables=>[["$name", "!Jack Ross!"], ["$finalwidth", "600"]])
PHP v1:
Copy to clipboard
cl_image_tag("greece_seaport.jpg", array("transformation"=>array("signed_photos"), "variables"=>array("$name"=>"!Jack Ross!", "$finalwidth"=>"600")))
PHP v2:
Copy to clipboard
(new ImageTag('greece_seaport.jpg'))
  ->addVariable(Variable::set('name', 'Jack Ross'))
  ->addVariable(Variable::set('finalwidth', 600))
  ->namedTransformation(NamedTransformation::name('signed_photos'));
Python:
Copy to clipboard
CloudinaryImage("greece_seaport.jpg").image(transformation=["signed_photos"], variables={"$name": "!Jack Ross!", "$finalwidth": "600"})
Node.js:
Copy to clipboard
cloudinary.image("greece_seaport.jpg", {transformation: ["signed_photos"], variables: [["$name", "!Jack Ross!"], ["$finalwidth", "600"]]})
Java:
Copy to clipboard
cloudinary.url().transformation(new Transformation()
.variables(variable("$name","!Jack Ross!"),variable("$finalwidth","600")).chain().named("signed_photos")).imageTag("greece_seaport.jpg");
JS:
Copy to clipboard
cloudinary.imageTag('greece_seaport.jpg', {transformation: ["signed_photos"], variables: [["$name", "!Jack Ross!"], ["$finalwidth", "600"]]}).toHtml();
jQuery:
Copy to clipboard
$.cloudinary.image("greece_seaport.jpg", {transformation: ["signed_photos"], variables: [["$name", "!Jack Ross!"], ["$finalwidth", "600"]]})
React:
Copy to clipboard
<Image publicId="greece_seaport.jpg" variables={[["$name", "!Jack Ross!"], ["$finalwidth", "600"]]}>
  <Transformation transformation={["signed_photos"]} />
</Image>
Vue.js:
Copy to clipboard
<cld-image publicId="greece_seaport.jpg" :variables="[['$name', '!Jack Ross!'], ['$finalwidth', '600']]">
  <cld-transformation transformation={["signed_photos"]} />
</cld-image>
Angular:
Copy to clipboard
<cl-image public-id="greece_seaport.jpg" variables="[['$name', '!Jack Ross!'], ['$finalwidth', '600']]">
  <cl-transformation transformation={{["signed_photos"]}}>
  </cl-transformation>
</cl-image>
.NET:
Copy to clipboard
cloudinary.Api.UrlImgUp.Transform(new Transformation().Named("signed_photos")).BuildImageTag("greece_seaport.jpg")
Android:
Copy to clipboard
MediaManager.get().url().transformation(new Transformation()
.variables(variable("$name","!Jack Ross!"),variable("$finalwidth","600")).chain().named("signed_photos")).generate("greece_seaport.jpg");
iOS:
Copy to clipboard
imageView.cldSetImage(cloudinary.createUrl().setTransformation(CLDTransformation().setNamed("signed_photos")).generate("greece_seaport.jpg")!, cloudinary: cloudinary)
photo signed using a named transformation with variables

Getting more creative

Now that you've got the idea, let's take a look at some more cool stuff you can achieve with variables.

Adjusting image size for art direction

Suppose you have a news site that always displays the two most recent news stories in the form of large square thumbnail links of 220x220 pixels, with 20 pixels between each. Below those, older news stories are displayed with small square image links (100x100), also with 20 pixels between each photo.

news story 123 news story 124
news story 101 news story 102 news story 103 news story 104

You could create a named transformation that sets the quality, crop type, gravity, and other image improvements, and additionally sets both the width and height to a $size variable that will be defined externally. For example, the t_news_square named transformation might be defined as: c_fill,e_improve,g_auto:faces,z_0.7,q_auto,w_$size,h_$size,

The delivery URLs for the large image thumbnails would set the $size variable to a value of 220:

Ruby:
Copy to clipboard
cl_image_tag("group.jpg", :transformation=>["news_square"], :variables=>[["$size", "220"]])
PHP v1:
Copy to clipboard
cl_image_tag("group.jpg", array("transformation"=>array("news_square"), "variables"=>array("$size"=>"220")))
PHP v2:
Copy to clipboard
(new ImageTag('group.jpg'))
  ->addVariable(Variable::set('size', 220))
  ->namedTransformation(NamedTransformation::name('news_square'));
Python:
Copy to clipboard
CloudinaryImage("group.jpg").image(transformation=["news_square"], variables={"$size": "220"})
Node.js:
Copy to clipboard
cloudinary.image("group.jpg", {transformation: ["news_square"], variables: [["$size", "220"]]})
Java:
Copy to clipboard
cloudinary.url().transformation(new Transformation()
.variables(variable("$size","220")).chain().named("news_square")).imageTag("group.jpg");
JS:
Copy to clipboard
cloudinary.imageTag('group.jpg', {transformation: ["news_square"], variables: [["$size", "220"]]}).toHtml();
jQuery:
Copy to clipboard
$.cloudinary.image("group.jpg", {transformation: ["news_square"], variables: [["$size", "220"]]})
React:
Copy to clipboard
<Image publicId="group.jpg" variables={[["$size", "220"]]}>
  <Transformation transformation={["news_square"]} />
</Image>
Vue.js:
Copy to clipboard
<cld-image publicId="group.jpg" :variables="[['$size', '220']]">
  <cld-transformation transformation={["news_square"]} />
</cld-image>
Angular:
Copy to clipboard
<cl-image public-id="group.jpg" variables="[['$size', '220']]">
  <cl-transformation transformation={{["news_square"]}}>
  </cl-transformation>
</cl-image>
.NET:
Copy to clipboard
cloudinary.Api.UrlImgUp.Transform(new Transformation().Named("news_square")).BuildImageTag("group.jpg")
Android:
Copy to clipboard
MediaManager.get().url().transformation(new Transformation()
.variables(variable("$size","220")).chain().named("news_square")).generate("group.jpg");
iOS:
Copy to clipboard
imageView.cldSetImage(cloudinary.createUrl().setTransformation(CLDTransformation().setNamed("news_square")).generate("group.jpg")!, cloudinary: cloudinary)

The transformation for the small images would reference the same named transformation, but with a size value of 100:

Ruby:
Copy to clipboard
cl_image_tag("partners_table.jpg", :transformation=>["news_square"], :variables=>[["$size", "100"]])
PHP v1:
Copy to clipboard
cl_image_tag("partners_table.jpg", array("transformation"=>array("news_square"), "variables"=>array("$size"=>"100")))
PHP v2:
Copy to clipboard
(new ImageTag('partners_table.jpg'))
  ->addVariable(Variable::set('size', 100))
  ->namedTransformation(NamedTransformation::name('news_square'));
Python:
Copy to clipboard
CloudinaryImage("partners_table.jpg").image(transformation=["news_square"], variables={"$size": "100"})
Node.js:
Copy to clipboard
cloudinary.image("partners_table.jpg", {transformation: ["news_square"], variables: [["$size", "100"]]})
Java:
Copy to clipboard
cloudinary.url().transformation(new Transformation()
.variables(variable("$size","100")).chain().named("news_square")).imageTag("partners_table.jpg");
JS:
Copy to clipboard
cloudinary.imageTag('partners_table.jpg', {transformation: ["news_square"], variables: [["$size", "100"]]}).toHtml();
jQuery:
Copy to clipboard
$.cloudinary.image("partners_table.jpg", {transformation: ["news_square"], variables: [["$size", "100"]]})
React:
Copy to clipboard
<Image publicId="partners_table.jpg" variables={[["$size", "100"]]}>
  <Transformation transformation={["news_square"]} />
</Image>
Vue.js:
Copy to clipboard
<cld-image publicId="partners_table.jpg" :variables="[['$size', '100']]">
  <cld-transformation transformation={["news_square"]} />
</cld-image>
Angular:
Copy to clipboard
<cl-image public-id="partners_table.jpg" variables="[['$size', '100']]">
  <cl-transformation transformation={{["news_square"]}}>
  </cl-transformation>
</cl-image>
.NET:
Copy to clipboard
cloudinary.Api.UrlImgUp.Transform(new Transformation().Named("news_square")).BuildImageTag("partners_table.jpg")
Android:
Copy to clipboard
MediaManager.get().url().transformation(new Transformation()
.variables(variable("$size","100")).chain().named("news_square")).generate("partners_table.jpg");
iOS:
Copy to clipboard
imageView.cldSetImage(cloudinary.createUrl().setTransformation(CLDTransformation().setNamed("news_square")).generate("partners_table.jpg")!, cloudinary: cloudinary)

Generating various profile pictures

Imagine that users upload a profile photo to your social media app and you need to display the photo on their profile page as a portrait-oriented rectangle with rounded corners, on their homepage as a square, and as a small circular chat head on the chat page. The zooming on each photo should adjust according to the size and shape of the image. And of course you know that as your site design changes in the future, these values might change.

But regardless of the above adjustments, you always want to use the same quality, format, and face recognition settings for all photos.

With variables, you simply create a named transformation (called profile_pic in our example) with static settings for the permanent elements, and variables for the size (aspect ratio & final width) and the radius (rounding):

Square photo for Home page Portrait-oriented round rectangle for profile page Small round headshot for chat window

The profile_pic named transformation looks like this:
$zoom_$aratio_sub_0.4/
ar_$aratio,c_thumb,f_auto,g_face,q_auto,r_$rounding,w_$width,z_$zoom

This named transformation takes advantage of arithmetic operators (subtraction in this case) to achieve a zoom that changes with the aspect ratio. Thus the 1:1 images (aspect ratio = 1) end up with a closer zoom, achieving a headshot, but not too close, while portrait images (aspect ratio < 1) have a smaller zoom, and thus show more of the whole body.

The round chat head picture shown above on the right would be delivered with a value of 1.0 for $aratio, 75 for $width, and 20 for $rounding:

Ruby:
Copy to clipboard
cl_image_tag("standing_woman.jpg", :transformation=>["profile_pic"], :variables=>[["$aratio", "1.0"], ["$width", "75"], ["$rounding", "200"]])
PHP v1:
Copy to clipboard
cl_image_tag("standing_woman.jpg", array("transformation"=>array("profile_pic"), "variables"=>array("$aratio"=>"1.0", "$width"=>"75", "$rounding"=>"200")))
PHP v2:
Copy to clipboard
(new ImageTag('standing_woman.jpg'))
  ->addVariable(Variable::set('aratio', 1.0))
  ->addVariable(Variable::set('width', 75))
  ->addVariable(Variable::set('rounding', 200))
  ->namedTransformation(NamedTransformation::name('profile_pic'));
Python:
Copy to clipboard
CloudinaryImage("standing_woman.jpg").image(transformation=["profile_pic"], variables={"$aratio": "1.0", "$width": "75", "$rounding": "200"})
Node.js:
Copy to clipboard
cloudinary.image("standing_woman.jpg", {transformation: ["profile_pic"], variables: [["$aratio", "1.0"], ["$width", "75"], ["$rounding", "200"]]})
Java:
Copy to clipboard
cloudinary.url().transformation(new Transformation()
.variables(variable("$aratio","1.0"),variable("$width","75"),variable("$rounding","200")).chain().named("profile_pic")).imageTag("standing_woman.jpg");
JS:
Copy to clipboard
cloudinary.imageTag('standing_woman.jpg', {transformation: ["profile_pic"], variables: [["$aratio", "1.0"], ["$width", "75"], ["$rounding", "200"]]}).toHtml();
jQuery:
Copy to clipboard
$.cloudinary.image("standing_woman.jpg", {transformation: ["profile_pic"], variables: [["$aratio", "1.0"], ["$width", "75"], ["$rounding", "200"]]})
React:
Copy to clipboard
<Image publicId="standing_woman.jpg" variables={[["$aratio", "1.0"], ["$width", "75"], ["$rounding", "200"]]}>
  <Transformation transformation={["profile_pic"]} />
</Image>
Vue.js:
Copy to clipboard
<cld-image publicId="standing_woman.jpg" :variables="[['$aratio', '1.0'], ['$width', '75'], ['$rounding', '200']]">
  <cld-transformation transformation={["profile_pic"]} />
</cld-image>
Angular:
Copy to clipboard
<cl-image public-id="standing_woman.jpg" variables="[['$aratio', '1.0'], ['$width', '75'], ['$rounding', '200']]">
  <cl-transformation transformation={{["profile_pic"]}}>
  </cl-transformation>
</cl-image>
.NET:
Copy to clipboard
cloudinary.Api.UrlImgUp.Transform(new Transformation().Named("profile_pic")).BuildImageTag("standing_woman.jpg")
Android:
Copy to clipboard
MediaManager.get().url().transformation(new Transformation()
.variables(variable("$aratio","1.0"),variable("$width","75"),variable("$rounding","200")).chain().named("profile_pic")).generate("standing_woman.jpg");
iOS:
Copy to clipboard
imageView.cldSetImage(cloudinary.createUrl().setTransformation(CLDTransformation().setNamed("profile_pic")).generate("standing_woman.jpg")!, cloudinary: cloudinary)

Going all out

Let's say you want to post the winners of a photo contest. The winning photos are delivered with a banner graphic overlay displaying the name of the winner and the place that he or she won. You want to post all photos at their original size, but both the width of the banner overlay and total width of the text (regardless of the length of the winners’ names) must be relative to the size of the photo.

For example:

Second place Second place Third place

You can accomplish this by creating user-defined variables for all the varying elements along with arithmetic operators and relative values. For example, we want the banner to be 90% of the photo's width and 15% of it's height. The text fits well on the banner when it's about 45% of the total image width, but the distance of the text from the bottom of the image has to be relative to the height of the resized banner. Of course we want the winner's name and where they placed, to be variables as well.

The transformation that achieves all this is somewhat complex, but that's OK. We just need to work it out once and then put it into a named transformation.

So the delivery of each winning photo is a snap!

For example, all we have to do to deliver the first place photo is set the $name variable to "Jen Fine" and the $award variable to "First":

Ruby:
Copy to clipboard
cl_image_tag("girl_camera.jpg", :transformation=>["photo_contest"], :variables=>[["$name", "!Jen Fine!"], ["$award", "!First!"]])
PHP v1:
Copy to clipboard
cl_image_tag("girl_camera.jpg", array("transformation"=>array("photo_contest"), "variables"=>array("$name"=>"!Jen Fine!", "$award"=>"!First!")))
PHP v2:
Copy to clipboard
(new ImageTag('girl_camera.jpg'))
  ->addVariable(Variable::set('name', 'Jen Fine'))
  ->addVariable(Variable::set('award', 'First'))
  ->namedTransformation(NamedTransformation::name('photo_contest'));
Python:
Copy to clipboard
CloudinaryImage("girl_camera.jpg").image(transformation=["photo_contest"], variables={"$name": "!Jen Fine!", "$award": "!First!"})
Node.js:
Copy to clipboard
cloudinary.image("girl_camera.jpg", {transformation: ["photo_contest"], variables: [["$name", "!Jen Fine!"], ["$award", "!First!"]]})
Java:
Copy to clipboard
cloudinary.url().transformation(new Transformation()
.variables(variable("$name","!Jen Fine!"),variable("$award","!First!")).chain().named("photo_contest")).imageTag("girl_camera.jpg");
JS:
Copy to clipboard
cloudinary.imageTag('girl_camera.jpg', {transformation: ["photo_contest"], variables: [["$name", "!Jen Fine!"], ["$award", "!First!"]]}).toHtml();
jQuery:
Copy to clipboard
$.cloudinary.image("girl_camera.jpg", {transformation: ["photo_contest"], variables: [["$name", "!Jen Fine!"], ["$award", "!First!"]]})
React:
Copy to clipboard
<Image publicId="girl_camera.jpg" variables={[["$name", "!Jen Fine!"], ["$award", "!First!"]]}>
  <Transformation transformation={["photo_contest"]} />
</Image>
Vue.js:
Copy to clipboard
<cld-image publicId="girl_camera.jpg" :variables="[['$name', '!Jen Fine!'], ['$award', '!First!']]">
  <cld-transformation transformation={["photo_contest"]} />
</cld-image>
Angular:
Copy to clipboard
<cl-image public-id="girl_camera.jpg" variables="[['$name', '!Jen Fine!'], ['$award', '!First!']]">
  <cl-transformation transformation={{["photo_contest"]}}>
  </cl-transformation>
</cl-image>
.NET:
Copy to clipboard
cloudinary.Api.UrlImgUp.Transform(new Transformation().Named("photo_contest")).BuildImageTag("girl_camera.jpg")
Android:
Copy to clipboard
MediaManager.get().url().transformation(new Transformation()
.variables(variable("$name","!Jen Fine!"),variable("$award","!First!")).chain().named("photo_contest")).generate("girl_camera.jpg");
iOS:
Copy to clipboard
imageView.cldSetImage(cloudinary.createUrl().setTransformation(CLDTransformation().setNamed("photo_contest")).generate("girl_camera.jpg")!, cloudinary: cloudinary)
First place for Jen Fine

Change is the only constant

"Change is the only constant." The phrase was coined by Greek philosophers over 2500 years ago, but it has never been truer than it is today. With the supersonic pace of Web technology development and non-stop improvements and new directions in UI design, having static values anywhere can be a hinderance.

But static values no longer need to weigh down your image transformation code. Take advantage of user-defined variables in named transformations to enable quick and flexible adjustments while maximizing code reuse and consistency across a variety of media assets.

This article demonstrated a bunch of ideas to get you started with user-defined variable transformations, but the sky's the limit. User-defined variables are supported with all Cloudinary plans, including the free one, so if you don't have a Cloudinary account yet, take a few seconds to get a free account now, and show us what you've got!

Recent Blog Posts

Automate the Staging Process of Videos for Social Media

Rich and engaging media helps build customer engagement and trust but can be time consuming to stage. Developers save a tremendous amount of time by preparing videos for social media with Cloudinary. That’s because Cloudinary’s interface, widgets, and application programming interface (API) transform raw media into polished content, optimizing footage and enabling effortless customization and publishing.

Read more

Top Five Web-Video Formats of 2021

By William Imoh
The Five Most Popular Web-Video Formats and Streaming Protocols

Over the past 15 years, the video industry has undergone a significant change in video formats on the web. In particular, in the early 2010s, the 3GP format, which the 3rd Generation Partnership Project (3GPP) created for 3G-enabled mobile devices, went nearly extinct. The advancement of mobile devices and cellular networks has brought about the need for pioneers to build better formats for a faster user experience.

Read more
Cloudinary Introduces Integration With SAP Commerce Cloud

We’re excited to announce Cloudinary’s integration with SAP Commerce Cloud, through which the latter’s customers can significantly boost the visual media experience on their website or app.

SAP Commerce Cloud powers some of the largest e-commerce sites (B2C, B2B, and B2B2C businesses), complete with building blocks like storefront design and order management. Reinforced with Cloudinary’s laser-sharp focus on optimizing, managing, and delivering images and videos, the new extension will enable SAP Commerce Cloud customers to create unique and engaging visual experiences effortlessly.

Read more
Personalizing Video Email for Marketing Campaigns With Cloudinary

As critical as it is to engage with shoppers in order to succeed in e-commerce, old-style, boring emails are far from being effective. In fact, they tend to be annoying because no one likes to read formulaic, generic messages that are sent en masse. For better results, rethink your email marketing campaigns and try out creative strategies.

Read more
Muted Videos and Subtitles

The bane of our existence is the lack of efficient ways for tackling the plethora of recurring tasks in our lives. One of those tasks is surfing the internet. We consume a lot of web content daily, of which a large percentage are images and videos. We’re constantly quickly scrolling through 30-second videos or checking out pictures of cute items we’d like to buy in our free time.

Read more

Building a Roommate-Matching App With Cloudinary and Jamstack

By Marcelo Ricardo de Oliveira
Building a Roommate-Matching App With Cloudinary and Jamstack

Roommate matching can be a pain—especially during the COVID pandemic when people don't want to meet in person. Matching apps like Flatmates, Roomster, and roommates.com are helpful, and if you're in the roommate-matching space, you know that great video is essential for those seeking roommates. Fortunately, Cloudinary can help.

Read more