Every image is unique and every one of your website visitors is different. In a perfect world, we'd like to adapt each image individually to be "just right" for every user. With "just right" being perfectly cropped, using responsive dimensions, correct encoding settings and optimal quality with the best image format.
Here’s a quick example using a photo of a (...drum roll...) cat:
Now, we need to adapt the image to fit the graphic design on every device and browser. The image should look great in any resolution and website layout and it should be delivered optimized to improve website performance.
Ruby:
cl_image_tag("white_cat.jpg", :client_hints=>true, :transformation=>[
{:aspect_ratio=>"4:6", :gravity=>"auto", :crop=>"fill"},
{:width=>"auto", :quality=>"auto", :fetch_format=>:auto, :crop=>"scale"}
])
PHP v1:
cl_image_tag("white_cat.jpg", array("client_hints"=>true, "transformation"=>array(
array("aspect_ratio"=>"4:6", "gravity"=>"auto", "crop"=>"fill"),
array("width"=>"auto", "quality"=>"auto", "fetch_format"=>"auto", "crop"=>"scale")
)))
PHP v2:
(new ImageTag('white_cat.jpg'))
->resize(Resize::fill()->aspectRatio('4:6')->gravity(Gravity::autoGravity()))
->resize(Resize::scale()->width('auto'))
->delivery(Delivery::format(Format::auto()))
->delivery(Delivery::quality(Quality::auto()))
->clientHints(true);
Python:
CloudinaryImage("white_cat.jpg").image(client_hints=True, transformation=[
{'aspect_ratio': "4:6", 'gravity': "auto", 'crop': "fill"},
{'width': "auto", 'quality': "auto", 'fetch_format': "auto", 'crop': "scale"}
])
Node.js:
cloudinary.image("white_cat.jpg", {client_hints: true, transformation: [
{aspect_ratio: "4:6", gravity: "auto", crop: "fill"},
{width: "auto", quality: "auto", fetch_format: "auto", crop: "scale"}
]})
Java:
cloudinary.url().transformation(new Transformation()
.aspectRatio("4:6").gravity("auto").crop("fill").chain()
.width("auto").quality("auto").fetchFormat("auto").crop("scale")).clientHints(true).imageTag("white_cat.jpg");
JS:
cloudinary.imageTag('white_cat.jpg', {clientHints: true, transformation: [
{aspectRatio: "4:6", gravity: "auto", crop: "fill"},
{width: "auto", quality: "auto", fetchFormat: "auto", crop: "scale"}
]}).toHtml();
jQuery:
$.cloudinary.image("white_cat.jpg", {client_hints: true, transformation: [
{aspect_ratio: "4:6", gravity: "auto", crop: "fill"},
{width: "auto", quality: "auto", fetch_format: "auto", crop: "scale"}
]})
React:
<Image publicId="white_cat.jpg" clientHints="true">
<Transformation aspectRatio="4:6" gravity="auto" crop="fill" />
<Transformation width="auto" quality="auto" fetchFormat="auto" crop="scale" />
</Image>
Vue.js:
<cld-image publicId="white_cat.jpg" clientHints="true">
<cld-transformation aspectRatio="4:6" gravity="auto" crop="fill" />
<cld-transformation width="auto" quality="auto" fetchFormat="auto" crop="scale" />
</cld-image>
Angular:
<cl-image public-id="white_cat.jpg" client-hints="true">
<cl-transformation aspect-ratio="4:6" gravity="auto" crop="fill">
</cl-transformation>
<cl-transformation width="auto" quality="auto" fetch-format="auto" crop="scale">
</cl-transformation>
</cl-image>
.NET:
cloudinary.Api.UrlImgUp.Transform(new Transformation()
.AspectRatio("4:6").Gravity("auto").Crop("fill").Chain()
.Width("auto").Quality("auto").FetchFormat("auto").Crop("scale")).ClientHints(true).BuildImageTag("white_cat.jpg")
Android:
MediaManager.get().url().transformation(new Transformation()
.aspectRatio("4:6").gravity("auto").crop("fill").chain()
.width("auto").quality("auto").fetchFormat("auto").crop("scale")).clientHints(true).generate("white_cat.jpg");
iOS:
imageView.cldSetImage(cloudinary.createUrl().setTransformation(CLDTransformation()
.setAspectRatio("4:6").setGravity("auto").setCrop("fill").chain()
.setWidth("auto").setQuality("auto").setFetchFormat("auto").setCrop("scale")).generate("white_cat.jpg")!, cloudinary: cloudinary)
The image on the left is a standard crop and is delivered as a standard JPEG of the original 2000px image height, resulting in a large 537KB file. However, the image on the right is perfectly cropped, scaled down to 300px wide and delivered as an optimized 17KB WebP image on Chrome (saving 97% of bandwidth).
Optimal delivery on an image-by-image and user-by-user basis is quite challenging. It requires a considerable know-how on the intricacies of file formats, vision algorithms and browser support. This challenge gets even more complicated when you want to scale up to hundreds of thousands if not hundreds of millions of images that may be uploaded to your websites and mobile apps.
So how is this achieved, and how can you automatically apply it to your own website and mobile apps?
When we launched the Cloudinary service more than 4 years ago, we wanted to eliminate the R&D time involved in managing the images of web and mobile applications. The service that was written by developers for developers, and was designed to be a complete solution for all their image needs. Today we are excited to introduce a major milestone in making our vision a reality by reaching the state of Images solved.
Developers want to create the perfect image crop focusing on the most important region. Developers need to determine the optimal file format and encoder settings for every image, to perfectly represent the content with the minimum number of bytes, for a fast and fluid user experience. Developers need to create multiple variants of every image for responsive delivery on various devices and in different resolutions.
Every image is unique in its content, and every visitor to your website has a different device, resolution, browser and network speed. It is challenging to deliver an optimal image to every user, especially when you need to scale that up to hundreds, thousands or millions of images.
The "Images Solved" solution introduced here, takes Cloudinary's cloud-based image management solution to the next level using automatic content-aware and context-aware image adaptation.
The concept is to let users upload one high resolution copy of any image and then automatically adapt the image, in real-time, to focus on the most important region of the image, select the optimal quality and encoding settings and responsively deliver the image to any device in any resolution or pixel density. We wanted to simplify the life of a developer further, by automating the following critical image manipulation and delivery components:
- Automatic content-aware cropping -
g_auto
- Intelligent content-aware encoding -
q_auto
- Dynamic format selection -
f_auto
- Automatic responsive images -
w_auto
and dpr_auto
For any website design, and specifically any responsive design, simply scaling an image up or down isn't always enough - images often require cropping to fit diverse device layouts, resolutions and aspect ratios while maximizing the visibility of important areas within the image.
Manually cropping individual images to fit them on various resolutions is resource intensive and quite impossible if your web application includes massive user generated content and a dynamic responsive layout.
Cropping all images to focus on the center of the image, by default, can result in a loss of important content.
Cloudinary already supports face detection to perfectly crop a photo, but what if you want to focus on more than just the face or if the image subject is a product, a food item or a pet?
The Cloudinary content-aware cropping algorithm uses a combination of heuristics to automatically detect the region of interest in every image and then crop them on the fly (using dynamic URLs) to fit the graphic design and layout of your site when viewed on any device, and without losing focus on important content.
For example, the following photo was uploaded to the cloud:
Now we need to adapt this image to fit in three different pages on a website: in landscape (e.g., 800x200), in portrait (e.g., 300x600) and as a square (e.g., 300x300). As you can see below, standard center cropping brings problematic results, as most of the cat is left out.
Setting the gravity
image cropping parameter to auto
or using g_auto
in the following on-the-fly image manipulation URL, generates the optimal cropping result, ensuring that the cat is always included.
Ruby:
cl_image_tag("sofa_cat.jpg", :width=>300, :height=>600, :gravity=>"auto", :crop=>"fill")
PHP v1:
cl_image_tag("sofa_cat.jpg", array("width"=>300, "height"=>600, "gravity"=>"auto", "crop"=>"fill"))
PHP v2:
(new ImageTag('sofa_cat.jpg'))
->resize(Resize::fill()->width(300)->height(600)
->gravity(Gravity::autoGravity()));
Python:
CloudinaryImage("sofa_cat.jpg").image(width=300, height=600, gravity="auto", crop="fill")
Node.js:
cloudinary.image("sofa_cat.jpg", {width: 300, height: 600, gravity: "auto", crop: "fill"})
Java:
cloudinary.url().transformation(new Transformation().width(300).height(600).gravity("auto").crop("fill")).imageTag("sofa_cat.jpg");
JS:
cloudinary.imageTag('sofa_cat.jpg', {width: 300, height: 600, gravity: "auto", crop: "fill"}).toHtml();
jQuery:
$.cloudinary.image("sofa_cat.jpg", {width: 300, height: 600, gravity: "auto", crop: "fill"})
React:
<Image publicId="sofa_cat.jpg" >
<Transformation width="300" height="600" gravity="auto" crop="fill" />
</Image>
Vue.js:
<cld-image publicId="sofa_cat.jpg" >
<cld-transformation width="300" height="600" gravity="auto" crop="fill" />
</cld-image>
Angular:
<cl-image public-id="sofa_cat.jpg" >
<cl-transformation width="300" height="600" gravity="auto" crop="fill">
</cl-transformation>
</cl-image>
.NET:
cloudinary.Api.UrlImgUp.Transform(new Transformation().Width(300).Height(600).Gravity("auto").Crop("fill")).BuildImageTag("sofa_cat.jpg")
Android:
MediaManager.get().url().transformation(new Transformation().width(300).height(600).gravity("auto").crop("fill")).generate("sofa_cat.jpg");
iOS:
imageView.cldSetImage(cloudinary.createUrl().setTransformation(CLDTransformation().setWidth(300).setHeight(600).setGravity("auto").setCrop("fill")).generate("sofa_cat.jpg")!, cloudinary: cloudinary)
The images above used the popular fill
crop mode, which tries to keep as much of the original image while cropping. However, sometimes websites prefer to display a more zoomed-in thumbnail emphasizing the person or object in the original image. For example, take a look at the following uploaded images:
For example, let's create a 300x270 thumbnail of each of the original images. The following images are dynamically created in the cloud using automatic face detection by setting gravity
to face
(g_face
).
Ruby:
cl_image_tag("sofa_cat.jpg", :width=>300, :height=>270, :gravity=>"face", :crop=>"thumb")
PHP v1:
cl_image_tag("sofa_cat.jpg", array("width"=>300, "height"=>270, "gravity"=>"face", "crop"=>"thumb"))
PHP v2:
(new ImageTag('sofa_cat.jpg'))
->resize(Resize::thumbnail()->width(300)->height(270)
->gravity(Gravity::focusOn(FocusOn::face())));
Python:
CloudinaryImage("sofa_cat.jpg").image(width=300, height=270, gravity="face", crop="thumb")
Node.js:
cloudinary.image("sofa_cat.jpg", {width: 300, height: 270, gravity: "face", crop: "thumb"})
Java:
cloudinary.url().transformation(new Transformation().width(300).height(270).gravity("face").crop("thumb")).imageTag("sofa_cat.jpg");
JS:
cloudinary.imageTag('sofa_cat.jpg', {width: 300, height: 270, gravity: "face", crop: "thumb"}).toHtml();
jQuery:
$.cloudinary.image("sofa_cat.jpg", {width: 300, height: 270, gravity: "face", crop: "thumb"})
React:
<Image publicId="sofa_cat.jpg" >
<Transformation width="300" height="270" gravity="face" crop="thumb" />
</Image>
Vue.js:
<cld-image publicId="sofa_cat.jpg" >
<cld-transformation width="300" height="270" gravity="face" crop="thumb" />
</cld-image>
Angular:
<cl-image public-id="sofa_cat.jpg" >
<cl-transformation width="300" height="270" gravity="face" crop="thumb">
</cl-transformation>
</cl-image>
.NET:
cloudinary.Api.UrlImgUp.Transform(new Transformation().Width(300).Height(270).Gravity("face").Crop("thumb")).BuildImageTag("sofa_cat.jpg")
Android:
MediaManager.get().url().transformation(new Transformation().width(300).height(270).gravity("face").crop("thumb")).generate("sofa_cat.jpg");
iOS:
imageView.cldSetImage(cloudinary.createUrl().setTransformation(CLDTransformation().setWidth(300).setHeight(270).setGravity("face").setCrop("thumb")).generate("sofa_cat.jpg")!, cloudinary: cloudinary)
The results above are nice, but they are not optimal thumbnails for the original photos. We see more of the blurry sofa instead of the nice cat, it's hard to see that there's a dog next to the lady, and no one will know that a red bike was in the original image.
Cloudinary's automatic cropping algorithm in the thumbnail generation mode focuses on the area of interest - not only on facial close ups - intelligently analyzing the image content. The thumbnails below are created by setting the crop mode to thumb
and the gravity
to auto
(c_thumb,g_auto
). These thumbnails effectively reflect the story behind each photo.
Ruby:
cl_image_tag("sofa_cat.jpg", :width=>300, :height=>270, :gravity=>"auto", :crop=>"thumb")
PHP v1:
cl_image_tag("sofa_cat.jpg", array("width"=>300, "height"=>270, "gravity"=>"auto", "crop"=>"thumb"))
PHP v2:
(new ImageTag('sofa_cat.jpg'))
->resize(Resize::thumbnail()->width(300)->height(270)
->gravity(Gravity::autoGravity()));
Python:
CloudinaryImage("sofa_cat.jpg").image(width=300, height=270, gravity="auto", crop="thumb")
Node.js:
cloudinary.image("sofa_cat.jpg", {width: 300, height: 270, gravity: "auto", crop: "thumb"})
Java:
cloudinary.url().transformation(new Transformation().width(300).height(270).gravity("auto").crop("thumb")).imageTag("sofa_cat.jpg");
JS:
cloudinary.imageTag('sofa_cat.jpg', {width: 300, height: 270, gravity: "auto", crop: "thumb"}).toHtml();
jQuery:
$.cloudinary.image("sofa_cat.jpg", {width: 300, height: 270, gravity: "auto", crop: "thumb"})
React:
<Image publicId="sofa_cat.jpg" >
<Transformation width="300" height="270" gravity="auto" crop="thumb" />
</Image>
Vue.js:
<cld-image publicId="sofa_cat.jpg" >
<cld-transformation width="300" height="270" gravity="auto" crop="thumb" />
</cld-image>
Angular:
<cl-image public-id="sofa_cat.jpg" >
<cl-transformation width="300" height="270" gravity="auto" crop="thumb">
</cl-transformation>
</cl-image>
.NET:
cloudinary.Api.UrlImgUp.Transform(new Transformation().Width(300).Height(270).Gravity("auto").Crop("thumb")).BuildImageTag("sofa_cat.jpg")
Android:
MediaManager.get().url().transformation(new Transformation().width(300).height(270).gravity("auto").crop("thumb")).generate("sofa_cat.jpg");
iOS:
imageView.cldSetImage(cloudinary.createUrl().setTransformation(CLDTransformation().setWidth(300).setHeight(270).setGravity("auto").setCrop("thumb")).generate("sofa_cat.jpg")!, cloudinary: cloudinary)
Try out the automatic cropping interactive demo page and see the 'Automatic cropping' documentation for more details.
Developers and designers tend to refrain from experimenting with image quality adjustments for fear of degrading the visual quality. However, adjusting the quality compression level doesn’t always lead to a loss of visual quality. In fact, precise adjustment of the compression level complemented by fine tuning of the encoding settings can significantly reduce the file size without any degradation noticeable to the human eye. So, what’s the standard quality setting that works for all images? Unfortunately, there is no single setting that is optimal for all images because it depends on a combination of the compression algorithm, the image format and the actual image content.
Cloudinary automates the file size versus quality trade-off decision, on the fly, by using perceptual metrics and heuristics that tune the encoding settings and select the appropriate image format based on the specific image content and format.
The intelligent encoding algorithm analyzes every image to find the best quality compression level and the optimal encoding settings based on the image content and the viewing browser, in order to produce a perceptually fine image while minimizing the file size.
For example, the following 500px wide image was encoded as a JPEG with a quality of 90 (without chroma subsampling). The image looks quite good and weighs 58.3KB.
Ruby:
cl_image_tag("cld_popsicles.jpg", :width=>500, :quality=>90, :crop=>"scale")
PHP v1:
cl_image_tag("cld_popsicles.jpg", array("width"=>500, "quality"=>90, "crop"=>"scale"))
PHP v2:
(new ImageTag('cld_popsicles.jpg'))
->resize(Resize::scale()->width(500))
->delivery(Delivery::quality(90));
Python:
CloudinaryImage("cld_popsicles.jpg").image(width=500, quality=90, crop="scale")
Node.js:
cloudinary.image("cld_popsicles.jpg", {width: 500, quality: 90, crop: "scale"})
Java:
cloudinary.url().transformation(new Transformation().width(500).quality(90).crop("scale")).imageTag("cld_popsicles.jpg");
JS:
cloudinary.imageTag('cld_popsicles.jpg', {width: 500, quality: 90, crop: "scale"}).toHtml();
jQuery:
$.cloudinary.image("cld_popsicles.jpg", {width: 500, quality: 90, crop: "scale"})
React:
<Image publicId="cld_popsicles.jpg" >
<Transformation width="500" quality="90" crop="scale" />
</Image>
Vue.js:
<cld-image publicId="cld_popsicles.jpg" >
<cld-transformation width="500" quality="90" crop="scale" />
</cld-image>
Angular:
<cl-image public-id="cld_popsicles.jpg" >
<cl-transformation width="500" quality="90" crop="scale">
</cl-transformation>
</cl-image>
.NET:
cloudinary.Api.UrlImgUp.Transform(new Transformation().Width(500).Quality(90).Crop("scale")).BuildImageTag("cld_popsicles.jpg")
Android:
MediaManager.get().url().transformation(new Transformation().width(500).quality(90).crop("scale")).generate("cld_popsicles.jpg");
iOS:
imageView.cldSetImage(cloudinary.createUrl().setTransformation(CLDTransformation().setWidth(500).setQuality(90).setCrop("scale")).generate("cld_popsicles.jpg")!, cloudinary: cloudinary)
Developers who want to optimize performance and save file size, might select a more aggressive compression quality level. However, as you can see below the resulting image might have disturbing artifacts, while the file size is smaller and weighs 24KB.
Ruby:
cl_image_tag("cld_popsicles.jpg", :width=>500, :quality=>50, :crop=>"scale")
PHP v1:
cl_image_tag("cld_popsicles.jpg", array("width"=>500, "quality"=>50, "crop"=>"scale"))
PHP v2:
(new ImageTag('cld_popsicles.jpg'))
->resize(Resize::scale()->width(500))
->delivery(Delivery::quality(50));
Python:
CloudinaryImage("cld_popsicles.jpg").image(width=500, quality=50, crop="scale")
Node.js:
cloudinary.image("cld_popsicles.jpg", {width: 500, quality: 50, crop: "scale"})
Java:
cloudinary.url().transformation(new Transformation().width(500).quality(50).crop("scale")).imageTag("cld_popsicles.jpg");
JS:
cloudinary.imageTag('cld_popsicles.jpg', {width: 500, quality: 50, crop: "scale"}).toHtml();
jQuery:
$.cloudinary.image("cld_popsicles.jpg", {width: 500, quality: 50, crop: "scale"})
React:
<Image publicId="cld_popsicles.jpg" >
<Transformation width="500" quality="50" crop="scale" />
</Image>
Vue.js:
<cld-image publicId="cld_popsicles.jpg" >
<cld-transformation width="500" quality="50" crop="scale" />
</cld-image>
Angular:
<cl-image public-id="cld_popsicles.jpg" >
<cl-transformation width="500" quality="50" crop="scale">
</cl-transformation>
</cl-image>
.NET:
cloudinary.Api.UrlImgUp.Transform(new Transformation().Width(500).Quality(50).Crop("scale")).BuildImageTag("cld_popsicles.jpg")
Android:
MediaManager.get().url().transformation(new Transformation().width(500).quality(50).crop("scale")).generate("cld_popsicles.jpg");
iOS:
imageView.cldSetImage(cloudinary.createUrl().setTransformation(CLDTransformation().setWidth(500).setQuality(50).setCrop("scale")).generate("cld_popsicles.jpg")!, cloudinary: cloudinary)
Now, setting the quality
manipulation parameter to auto
(q_auto
) tells Cloudinary to analyze the image and find the optimal image encoding settings and compression quality level. The resulting image looks quite good and weighs only 39.6KB, which means a saving of 32% over a standard 90 quality encoded image.
Ruby:
cl_image_tag("cld_popsicles.jpg", :width=>500, :quality=>"auto", :crop=>"scale")
PHP v1:
cl_image_tag("cld_popsicles.jpg", array("width"=>500, "quality"=>"auto", "crop"=>"scale"))
PHP v2:
(new ImageTag('cld_popsicles.jpg'))
->resize(Resize::scale()->width(500))
->delivery(Delivery::quality(Quality::auto()));
Python:
CloudinaryImage("cld_popsicles.jpg").image(width=500, quality="auto", crop="scale")
Node.js:
cloudinary.image("cld_popsicles.jpg", {width: 500, quality: "auto", crop: "scale"})
Java:
cloudinary.url().transformation(new Transformation().width(500).quality("auto").crop("scale")).imageTag("cld_popsicles.jpg");
JS:
cloudinary.imageTag('cld_popsicles.jpg', {width: 500, quality: "auto", crop: "scale"}).toHtml();
jQuery:
$.cloudinary.image("cld_popsicles.jpg", {width: 500, quality: "auto", crop: "scale"})
React:
<Image publicId="cld_popsicles.jpg" >
<Transformation width="500" quality="auto" crop="scale" />
</Image>
Vue.js:
<cld-image publicId="cld_popsicles.jpg" >
<cld-transformation width="500" quality="auto" crop="scale" />
</cld-image>
Angular:
<cl-image public-id="cld_popsicles.jpg" >
<cl-transformation width="500" quality="auto" crop="scale">
</cl-transformation>
</cl-image>
.NET:
cloudinary.Api.UrlImgUp.Transform(new Transformation().Width(500).Quality("auto").Crop("scale")).BuildImageTag("cld_popsicles.jpg")
Android:
MediaManager.get().url().transformation(new Transformation().width(500).quality("auto").crop("scale")).generate("cld_popsicles.jpg");
iOS:
imageView.cldSetImage(cloudinary.createUrl().setTransformation(CLDTransformation().setWidth(500).setQuality("auto").setCrop("scale")).generate("cld_popsicles.jpg")!, cloudinary: cloudinary)
The significance of the performance optimization depends on the content of every individual image. For example, the following image weighs 63.7KB using regular JPEG encoding with a quality of 90, and only 25.1KB (a 61% saving) with quality set to auto
, while still looking great.
Furthermore, when a user enables data saving mode, the browser sends the Save-Data: on
Client Hint request header, and Cloudinary automatically switches to a more aggressive automatic quality level and encoding settings selection.
Try out the automatic quality interactive demo page and see the Automatic quality and encoding settings documentation for more details.
Developers are expected to select optimal image formats for different scenarios based on the image content and the viewing device/browser. For example, JPEG should be used for a captured photograph or for faster loading while PNG should be used for illustrations or drawings or when using a transparent background. Additional logic should also be considered for modern formats such as WebP and JPEG-XR, if the viewing browser is Chrome or Internet-Explorer/Edge.
Image formats can have a significant impact on the page load time and bandwidth - for example using WebP over JPEG can result in a 30% file size reduction, which can lead to faster page loads resulting in improved site engagement and conversion rates.
The browser and format compatibility requirements seem simple, but manually adopting the format logic for many images can be complex and inefficient.
To optimize image delivery, Cloudinary can dynamically select the most efficient format, based on the content and viewing browser, and convert the image to that format on the fly. For example, it automatically delivers images as WebP to Chrome or JPEG-XR to Internet Explorer or Edge. No more manual conversion of images to the most efficient formats.
Transparent WebP delivery and JPEG-XR delivery were introduced a couple of years ago. Now, you can combine automatic format selection with automatic image encoding and quality selection. For example, the same image as above with q_auto,f_auto
will be encoded as WebP for Chrome users, consuming only 16.2KB and still look great. Users of Internet-Explorer/Edge will get a JPEG-XR and users of other browsers will get a JPEG.
Ruby:
cl_image_tag("sofa_cat.jpg", :width=>750, :quality=>"auto", :fetch_format=>:auto, :crop=>"scale")
PHP v1:
cl_image_tag("sofa_cat.jpg", array("width"=>750, "quality"=>"auto", "fetch_format"=>"auto", "crop"=>"scale"))
PHP v2:
(new ImageTag('sofa_cat.jpg'))
->resize(Resize::scale()->width(750))
->delivery(Delivery::format(Format::auto()))
->delivery(Delivery::quality(Quality::auto()));
Python:
CloudinaryImage("sofa_cat.jpg").image(width=750, quality="auto", fetch_format="auto", crop="scale")
Node.js:
cloudinary.image("sofa_cat.jpg", {width: 750, quality: "auto", fetch_format: "auto", crop: "scale"})
Java:
cloudinary.url().transformation(new Transformation().width(750).quality("auto").fetchFormat("auto").crop("scale")).imageTag("sofa_cat.jpg");
JS:
cloudinary.imageTag('sofa_cat.jpg', {width: 750, quality: "auto", fetchFormat: "auto", crop: "scale"}).toHtml();
jQuery:
$.cloudinary.image("sofa_cat.jpg", {width: 750, quality: "auto", fetch_format: "auto", crop: "scale"})
React:
<Image publicId="sofa_cat.jpg" >
<Transformation width="750" quality="auto" fetchFormat="auto" crop="scale" />
</Image>
Vue.js:
<cld-image publicId="sofa_cat.jpg" >
<cld-transformation width="750" quality="auto" fetchFormat="auto" crop="scale" />
</cld-image>
Angular:
<cl-image public-id="sofa_cat.jpg" >
<cl-transformation width="750" quality="auto" fetch-format="auto" crop="scale">
</cl-transformation>
</cl-image>
.NET:
cloudinary.Api.UrlImgUp.Transform(new Transformation().Width(750).Quality("auto").FetchFormat("auto").Crop("scale")).BuildImageTag("sofa_cat.jpg")
Android:
MediaManager.get().url().transformation(new Transformation().width(750).quality("auto").fetchFormat("auto").crop("scale")).generate("sofa_cat.jpg");
iOS:
imageView.cldSetImage(cloudinary.createUrl().setTransformation(CLDTransformation().setWidth(750).setQuality("auto").setFetchFormat("auto").setCrop("scale")).generate("sofa_cat.jpg")!, cloudinary: cloudinary)
The combination of q_auto
and f_auto
is even more powerful together than either of them separately. The algorithm might detect that the PNG format (or the lossless WebP format) is a better fit for specific images that contain content such as drawings. For some images, even the PNG8 format can be automatically selected for providing great looking results with a very efficient file size.
For example, the following URL dynamically generates a 500px wide version of a drawing using automatic image encoding and quality selection (q_auto
without f_auto
).
Ruby:
cl_image_tag("flowers_and_birds.jpg", :width=>500, :quality=>"auto", :crop=>"scale")
PHP v1:
cl_image_tag("flowers_and_birds.jpg", array("width"=>500, "quality"=>"auto", "crop"=>"scale"))
PHP v2:
(new ImageTag('flowers_and_birds.jpg'))
->resize(Resize::scale()->width(500))
->delivery(Delivery::quality(Quality::auto()));
Python:
CloudinaryImage("flowers_and_birds.jpg").image(width=500, quality="auto", crop="scale")
Node.js:
cloudinary.image("flowers_and_birds.jpg", {width: 500, quality: "auto", crop: "scale"})
Java:
cloudinary.url().transformation(new Transformation().width(500).quality("auto").crop("scale")).imageTag("flowers_and_birds.jpg");
JS:
cloudinary.imageTag('flowers_and_birds.jpg', {width: 500, quality: "auto", crop: "scale"}).toHtml();
jQuery:
$.cloudinary.image("flowers_and_birds.jpg", {width: 500, quality: "auto", crop: "scale"})
React:
<Image publicId="flowers_and_birds.jpg" >
<Transformation width="500" quality="auto" crop="scale" />
</Image>
Vue.js:
<cld-image publicId="flowers_and_birds.jpg" >
<cld-transformation width="500" quality="auto" crop="scale" />
</cld-image>
Angular:
<cl-image public-id="flowers_and_birds.jpg" >
<cl-transformation width="500" quality="auto" crop="scale">
</cl-transformation>
</cl-image>
.NET:
cloudinary.Api.UrlImgUp.Transform(new Transformation().Width(500).Quality("auto").Crop("scale")).BuildImageTag("flowers_and_birds.jpg")
Android:
MediaManager.get().url().transformation(new Transformation().width(500).quality("auto").crop("scale")).generate("flowers_and_birds.jpg");
iOS:
imageView.cldSetImage(cloudinary.createUrl().setTransformation(CLDTransformation().setWidth(500).setQuality("auto").setCrop("scale")).generate("flowers_and_birds.jpg")!, cloudinary: cloudinary)
The result is a JPEG image that weighs 41KB. If you look carefully, you will see that the lossy nature of JPEG resulted in some unpleasant artifacts. Now, let's tell Cloudinary to combine both q_auto
and f_auto
. The result is shown below. The algorithm decided to encode the image using the PNG8 format. The image looks better, has no artifacts (the only artifacts are from the original high-quality JPEG image), and weighs even less - just 34.8KB.
Ruby:
cl_image_tag("flowers_and_birds.jpg", :width=>500, :quality=>"auto", :fetch_format=>:auto, :crop=>"scale")
PHP v1:
cl_image_tag("flowers_and_birds.jpg", array("width"=>500, "quality"=>"auto", "fetch_format"=>"auto", "crop"=>"scale"))
PHP v2:
(new ImageTag('flowers_and_birds.jpg'))
->resize(Resize::scale()->width(500))
->delivery(Delivery::format(Format::auto()))
->delivery(Delivery::quality(Quality::auto()));
Python:
CloudinaryImage("flowers_and_birds.jpg").image(width=500, quality="auto", fetch_format="auto", crop="scale")
Node.js:
cloudinary.image("flowers_and_birds.jpg", {width: 500, quality: "auto", fetch_format: "auto", crop: "scale"})
Java:
cloudinary.url().transformation(new Transformation().width(500).quality("auto").fetchFormat("auto").crop("scale")).imageTag("flowers_and_birds.jpg");
JS:
cloudinary.imageTag('flowers_and_birds.jpg', {width: 500, quality: "auto", fetchFormat: "auto", crop: "scale"}).toHtml();
jQuery:
$.cloudinary.image("flowers_and_birds.jpg", {width: 500, quality: "auto", fetch_format: "auto", crop: "scale"})
React:
<Image publicId="flowers_and_birds.jpg" >
<Transformation width="500" quality="auto" fetchFormat="auto" crop="scale" />
</Image>
Vue.js:
<cld-image publicId="flowers_and_birds.jpg" >
<cld-transformation width="500" quality="auto" fetchFormat="auto" crop="scale" />
</cld-image>
Angular:
<cl-image public-id="flowers_and_birds.jpg" >
<cl-transformation width="500" quality="auto" fetch-format="auto" crop="scale">
</cl-transformation>
</cl-image>
.NET:
cloudinary.Api.UrlImgUp.Transform(new Transformation().Width(500).Quality("auto").FetchFormat("auto").Crop("scale")).BuildImageTag("flowers_and_birds.jpg")
Android:
MediaManager.get().url().transformation(new Transformation().width(500).quality("auto").fetchFormat("auto").crop("scale")).generate("flowers_and_birds.jpg");
iOS:
imageView.cldSetImage(cloudinary.createUrl().setTransformation(CLDTransformation().setWidth(500).setQuality("auto").setFetchFormat("auto").setCrop("scale")).generate("flowers_and_birds.jpg")!, cloudinary: cloudinary)
See Automatic format selection documentation page for more details.
To implement responsive design, developers need to create multiple versions of every image to make it look perfect on any device, in any resolution, pixel density or orientation.
Generating, managing, marking-up, and delivering these numerous image versions can be a daunting task. Cloudinary simplifies dynamic image delivery for responsive websites on Retina and regular displays by automating the image width and DPR value decision based on the viewing device, display size and layout.
Using Google's Client Hints, Cloudinary determines the required width of an image based on the browser's viewport-width or the layout width, and then calculates the optimal resolution for displaying the image on that device. Furthermore, Cloudinary can dynamically select image-specific breakpoints by determining the required number of versions of every image in order to balance the optimal dimensions vs. bandwidth reduction trade-off.
This means that developers simply need to have a single high resolution version of every image. Cloudinary automatically adapts the image to fit the viewport, layout and resolution on any device using a single dynamic URL - ensuring a visually seamless user experience while improving performance.
Generating images of different width and DPR values via the same dynamic URL is done by setting the width
and dpr
manipulation parameters to auto
(dpr_auto,w_auto
). The Client Hints request headers are automatically processed to retrieve the best fitting image dimensions.
For example, the following URL first crops an image to a 16:9 aspect ratio and requests automatic DPR and width delivery. The device's DPR is rounded up to an integer value (1.0, 2.0, 3.0, etc.) and the actual required width value is rounded up to the closest multiple of 100 pixels (by default). The example below also limits the delivered image to 2000px wide in case that the Client Hints are not available.
Ruby:
cl_image_tag("car_lady_dog.jpg", :client_hints=>true, :transformation=>[
{:gravity=>"auto", :aspect_ratio=>"16:9", :crop=>"fill"},
{:dpr=>"auto", :width=>"auto", :crop=>"scale"},
{:width=>2000, :crop=>"limit"}
])
PHP v1:
cl_image_tag("car_lady_dog.jpg", array("client_hints"=>true, "transformation"=>array(
array("gravity"=>"auto", "aspect_ratio"=>"16:9", "crop"=>"fill"),
array("dpr"=>"auto", "width"=>"auto", "crop"=>"scale"),
array("width"=>2000, "crop"=>"limit")
)))
PHP v2:
(new ImageTag('car_lady_dog.jpg'))
->resize(Resize::fill()->aspectRatio(AspectRatio::ar16X9())
->gravity(Gravity::autoGravity()))
->resize(Resize::scale()->width('auto'))
->delivery(Delivery::dpr(Dpr::auto()))
->resize(Resize::limitFit()->width(2000))
->clientHints(true);
Python:
CloudinaryImage("car_lady_dog.jpg").image(client_hints=True, transformation=[
{'gravity': "auto", 'aspect_ratio': "16:9", 'crop': "fill"},
{'dpr': "auto", 'width': "auto", 'crop': "scale"},
{'width': 2000, 'crop': "limit"}
])
Node.js:
cloudinary.image("car_lady_dog.jpg", {client_hints: true, transformation: [
{gravity: "auto", aspect_ratio: "16:9", crop: "fill"},
{dpr: "auto", width: "auto", crop: "scale"},
{width: 2000, crop: "limit"}
]})
Java:
cloudinary.url().transformation(new Transformation()
.gravity("auto").aspectRatio("16:9").crop("fill").chain()
.dpr("auto").width("auto").crop("scale").chain()
.width(2000).crop("limit")).clientHints(true).imageTag("car_lady_dog.jpg");
JS:
cloudinary.imageTag('car_lady_dog.jpg', {clientHints: true, transformation: [
{gravity: "auto", aspectRatio: "16:9", crop: "fill"},
{dpr: "auto", width: "auto", crop: "scale"},
{width: 2000, crop: "limit"}
]}).toHtml();
jQuery:
$.cloudinary.image("car_lady_dog.jpg", {client_hints: true, transformation: [
{gravity: "auto", aspect_ratio: "16:9", crop: "fill"},
{dpr: "auto", width: "auto", crop: "scale"},
{width: 2000, crop: "limit"}
]})
React:
<Image publicId="car_lady_dog.jpg" clientHints="true">
<Transformation gravity="auto" aspectRatio="16:9" crop="fill" />
<Transformation dpr="auto" width="auto" crop="scale" />
<Transformation width="2000" crop="limit" />
</Image>
Vue.js:
<cld-image publicId="car_lady_dog.jpg" clientHints="true">
<cld-transformation gravity="auto" aspectRatio="16:9" crop="fill" />
<cld-transformation dpr="auto" width="auto" crop="scale" />
<cld-transformation width="2000" crop="limit" />
</cld-image>
Angular:
<cl-image public-id="car_lady_dog.jpg" client-hints="true">
<cl-transformation gravity="auto" aspect-ratio="16:9" crop="fill">
</cl-transformation>
<cl-transformation dpr="auto" width="auto" crop="scale">
</cl-transformation>
<cl-transformation width="2000" crop="limit">
</cl-transformation>
</cl-image>
.NET:
cloudinary.Api.UrlImgUp.Transform(new Transformation()
.Gravity("auto").AspectRatio("16:9").Crop("fill").Chain()
.Dpr("auto").Width("auto").Crop("scale").Chain()
.Width(2000).Crop("limit")).ClientHints(true).BuildImageTag("car_lady_dog.jpg")
Android:
MediaManager.get().url().transformation(new Transformation()
.gravity("auto").aspectRatio("16:9").crop("fill").chain()
.dpr("auto").width("auto").crop("scale").chain()
.width(2000).crop("limit")).clientHints(true).generate("car_lady_dog.jpg");
iOS:
imageView.cldSetImage(cloudinary.createUrl().setTransformation(CLDTransformation()
.setGravity("auto").setAspectRatio("16:9").setCrop("fill").chain()
.setDpr("auto").setWidth("auto").setCrop("scale").chain()
.setWidth(2000).setCrop("limit")).generate("car_lady_dog.jpg")!, cloudinary: cloudinary)
The following URL simulates the Client Hints header, specifying a fallback width of 387 pixels, which is used in the case that the Client Hint header is not available. In this case, the generated image is rounded up to 400 pixels wide, which weighs only 29.9KB compared to the 414KB of the the maximum 2000px wide image.
Ruby:
cl_image_tag("car_lady_dog.jpg", :client_hints=>true, :transformation=>[
{:gravity=>"auto", :aspect_ratio=>"16:9", :crop=>"fill"},
{:width=>"auto:100:387", :crop=>"scale"}
])
PHP v1:
cl_image_tag("car_lady_dog.jpg", array("client_hints"=>true, "transformation"=>array(
array("gravity"=>"auto", "aspect_ratio"=>"16:9", "crop"=>"fill"),
array("width"=>"auto:100:387", "crop"=>"scale")
)))
PHP v2:
(new ImageTag('car_lady_dog.jpg'))
->resize(Resize::fill()->aspectRatio(AspectRatio::ar16X9())
->gravity(Gravity::autoGravity()))
->resize(Resize::scale()->width('auto:100:387'))
->clientHints(true);
Python:
CloudinaryImage("car_lady_dog.jpg").image(client_hints=True, transformation=[
{'gravity': "auto", 'aspect_ratio': "16:9", 'crop': "fill"},
{'width': "auto:100:387", 'crop': "scale"}
])
Node.js:
cloudinary.image("car_lady_dog.jpg", {client_hints: true, transformation: [
{gravity: "auto", aspect_ratio: "16:9", crop: "fill"},
{width: "auto:100:387", crop: "scale"}
]})
Java:
cloudinary.url().transformation(new Transformation()
.gravity("auto").aspectRatio("16:9").crop("fill").chain()
.width("auto:100:387").crop("scale")).clientHints(true).imageTag("car_lady_dog.jpg");
JS:
cloudinary.imageTag('car_lady_dog.jpg', {clientHints: true, transformation: [
{gravity: "auto", aspectRatio: "16:9", crop: "fill"},
{width: "auto:100:387", crop: "scale"}
]}).toHtml();
jQuery:
$.cloudinary.image("car_lady_dog.jpg", {client_hints: true, transformation: [
{gravity: "auto", aspect_ratio: "16:9", crop: "fill"},
{width: "auto:100:387", crop: "scale"}
]})
React:
<Image publicId="car_lady_dog.jpg" clientHints="true">
<Transformation gravity="auto" aspectRatio="16:9" crop="fill" />
<Transformation width="auto:100:387" crop="scale" />
</Image>
Vue.js:
<cld-image publicId="car_lady_dog.jpg" clientHints="true">
<cld-transformation gravity="auto" aspectRatio="16:9" crop="fill" />
<cld-transformation width="auto:100:387" crop="scale" />
</cld-image>
Angular:
<cl-image public-id="car_lady_dog.jpg" client-hints="true">
<cl-transformation gravity="auto" aspect-ratio="16:9" crop="fill">
</cl-transformation>
<cl-transformation width="auto:100:387" crop="scale">
</cl-transformation>
</cl-image>
.NET:
cloudinary.Api.UrlImgUp.Transform(new Transformation()
.Gravity("auto").AspectRatio("16:9").Crop("fill").Chain()
.Width("auto:100:387").Crop("scale")).ClientHints(true).BuildImageTag("car_lady_dog.jpg")
Android:
MediaManager.get().url().transformation(new Transformation()
.gravity("auto").aspectRatio("16:9").crop("fill").chain()
.width("auto:100:387").crop("scale")).clientHints(true).generate("car_lady_dog.jpg");
iOS:
imageView.cldSetImage(cloudinary.createUrl().setTransformation(CLDTransformation()
.setGravity("auto").setAspectRatio("16:9").setCrop("fill").chain()
.setWidth("auto:100:387").setCrop("scale")).generate("car_lady_dog.jpg")!, cloudinary: cloudinary)
One image for all screen resolutions and different devices is not enough. An image for every pixel change in width is too much, while an image for every 100 pixels difference might be an arbitrary threshold - so how can someone automatically choose the optimal responsive image sizes?
Fortunately, Cloudinary's new automatic responsive images solution now supports dynamic generation of optimal breakpoints for each individual image on-the-fly. Here's an example of a URL that calculates and returns the optimal breakpoints as shown below.
Ruby:
cl_image_tag("car_lady_dog.jpg", :client_hints=>true, :transformation=>[
{:gravity=>"auto", :aspect_ratio=>"16:9", :crop=>"fill"},
{:width=>"auto:breakpoints:json", :crop=>"scale"}
])
PHP v1:
cl_image_tag("car_lady_dog.jpg", array("client_hints"=>true, "transformation"=>array(
array("gravity"=>"auto", "aspect_ratio"=>"16:9", "crop"=>"fill"),
array("width"=>"auto:breakpoints:json", "crop"=>"scale")
)))
PHP v2:
(new ImageTag('car_lady_dog.jpg'))
->resize(Resize::fill()->aspectRatio(AspectRatio::ar16X9())
->gravity(Gravity::autoGravity()))
->resize(Resize::scale()->width('auto:breakpoints:json'))
->clientHints(true);
Python:
CloudinaryImage("car_lady_dog.jpg").image(client_hints=True, transformation=[
{'gravity': "auto", 'aspect_ratio': "16:9", 'crop': "fill"},
{'width': "auto:breakpoints:json", 'crop': "scale"}
])
Node.js:
cloudinary.image("car_lady_dog.jpg", {client_hints: true, transformation: [
{gravity: "auto", aspect_ratio: "16:9", crop: "fill"},
{width: "auto:breakpoints:json", crop: "scale"}
]})
Java:
cloudinary.url().transformation(new Transformation()
.gravity("auto").aspectRatio("16:9").crop("fill").chain()
.width("auto:breakpoints:json").crop("scale")).clientHints(true).imageTag("car_lady_dog.jpg");
JS:
cloudinary.imageTag('car_lady_dog.jpg', {clientHints: true, transformation: [
{gravity: "auto", aspectRatio: "16:9", crop: "fill"},
{width: "auto:breakpoints:json", crop: "scale"}
]}).toHtml();
jQuery:
$.cloudinary.image("car_lady_dog.jpg", {client_hints: true, transformation: [
{gravity: "auto", aspect_ratio: "16:9", crop: "fill"},
{width: "auto:breakpoints:json", crop: "scale"}
]})
React:
<Image publicId="car_lady_dog.jpg" clientHints="true">
<Transformation gravity="auto" aspectRatio="16:9" crop="fill" />
<Transformation width="auto:breakpoints:json" crop="scale" />
</Image>
Vue.js:
<cld-image publicId="car_lady_dog.jpg" clientHints="true">
<cld-transformation gravity="auto" aspectRatio="16:9" crop="fill" />
<cld-transformation width="auto:breakpoints:json" crop="scale" />
</cld-image>
Angular:
<cl-image public-id="car_lady_dog.jpg" client-hints="true">
<cl-transformation gravity="auto" aspect-ratio="16:9" crop="fill">
</cl-transformation>
<cl-transformation width="auto:breakpoints:json" crop="scale">
</cl-transformation>
</cl-image>
.NET:
cloudinary.Api.UrlImgUp.Transform(new Transformation()
.Gravity("auto").AspectRatio("16:9").Crop("fill").Chain()
.Width("auto:breakpoints:json").Crop("scale")).ClientHints(true).BuildImageTag("car_lady_dog.jpg")
Android:
MediaManager.get().url().transformation(new Transformation()
.gravity("auto").aspectRatio("16:9").crop("fill").chain()
.width("auto:breakpoints:json").crop("scale")).clientHints(true).generate("car_lady_dog.jpg");
iOS:
imageView.cldSetImage(cloudinary.createUrl().setTransformation(CLDTransformation()
.setGravity("auto").setAspectRatio("16:9").setCrop("fill").chain()
.setWidth("auto:breakpoints:json").setCrop("scale")).generate("car_lady_dog.jpg")!, cloudinary: cloudinary)
{"breakpoints":[50,335,507,648,793,919,1000]}
The breakpoints are seamlessly used behind the scenes, so a single URL like the following one will return an image with a width equal to the breakpoint closest to the required display dimensions. For example, if the required width is 387px, an image that is 507 pixels wide will be dynamically generated in the cloud and delivered to users.
Ruby:
cl_image_tag("car_lady_dog.jpg", :client_hints=>true, :transformation=>[
{:gravity=>"auto", :aspect_ratio=>"16:9", :crop=>"fill"},
{:dpr=>"auto"},
{:width=>"auto:breakpoints:387", :crop=>"scale"}
])
PHP v1:
cl_image_tag("car_lady_dog.jpg", array("client_hints"=>true, "transformation"=>array(
array("gravity"=>"auto", "aspect_ratio"=>"16:9", "crop"=>"fill"),
array("dpr"=>"auto"),
array("width"=>"auto:breakpoints:387", "crop"=>"scale")
)))
PHP v2:
(new ImageTag('car_lady_dog.jpg'))
->resize(Resize::fill()->aspectRatio(AspectRatio::ar16X9())
->gravity(Gravity::autoGravity()))
->delivery(Delivery::dpr(Dpr::auto()))
->resize(Resize::scale()->width('auto:breakpoints:387'))
->clientHints(true);
Python:
CloudinaryImage("car_lady_dog.jpg").image(client_hints=True, transformation=[
{'gravity': "auto", 'aspect_ratio': "16:9", 'crop': "fill"},
{'dpr': "auto"},
{'width': "auto:breakpoints:387", 'crop': "scale"}
])
Node.js:
cloudinary.image("car_lady_dog.jpg", {client_hints: true, transformation: [
{gravity: "auto", aspect_ratio: "16:9", crop: "fill"},
{dpr: "auto"},
{width: "auto:breakpoints:387", crop: "scale"}
]})
Java:
cloudinary.url().transformation(new Transformation()
.gravity("auto").aspectRatio("16:9").crop("fill").chain()
.dpr("auto").chain()
.width("auto:breakpoints:387").crop("scale")).clientHints(true).imageTag("car_lady_dog.jpg");
JS:
cloudinary.imageTag('car_lady_dog.jpg', {clientHints: true, transformation: [
{gravity: "auto", aspectRatio: "16:9", crop: "fill"},
{dpr: "auto"},
{width: "auto:breakpoints:387", crop: "scale"}
]}).toHtml();
jQuery:
$.cloudinary.image("car_lady_dog.jpg", {client_hints: true, transformation: [
{gravity: "auto", aspect_ratio: "16:9", crop: "fill"},
{dpr: "auto"},
{width: "auto:breakpoints:387", crop: "scale"}
]})
React:
<Image publicId="car_lady_dog.jpg" clientHints="true">
<Transformation gravity="auto" aspectRatio="16:9" crop="fill" />
<Transformation dpr="auto" />
<Transformation width="auto:breakpoints:387" crop="scale" />
</Image>
Vue.js:
<cld-image publicId="car_lady_dog.jpg" clientHints="true">
<cld-transformation gravity="auto" aspectRatio="16:9" crop="fill" />
<cld-transformation dpr="auto" />
<cld-transformation width="auto:breakpoints:387" crop="scale" />
</cld-image>
Angular:
<cl-image public-id="car_lady_dog.jpg" client-hints="true">
<cl-transformation gravity="auto" aspect-ratio="16:9" crop="fill">
</cl-transformation>
<cl-transformation dpr="auto">
</cl-transformation>
<cl-transformation width="auto:breakpoints:387" crop="scale">
</cl-transformation>
</cl-image>
.NET:
cloudinary.Api.UrlImgUp.Transform(new Transformation()
.Gravity("auto").AspectRatio("16:9").Crop("fill").Chain()
.Dpr("auto").Chain()
.Width("auto:breakpoints:387").Crop("scale")).ClientHints(true).BuildImageTag("car_lady_dog.jpg")
Android:
MediaManager.get().url().transformation(new Transformation()
.gravity("auto").aspectRatio("16:9").crop("fill").chain()
.dpr("auto").chain()
.width("auto:breakpoints:387").crop("scale")).clientHints(true).generate("car_lady_dog.jpg");
iOS:
imageView.cldSetImage(cloudinary.createUrl().setTransformation(CLDTransformation()
.setGravity("auto").setAspectRatio("16:9").setCrop("fill").chain()
.setDpr("auto").chain()
.setWidth("auto:breakpoints:387").setCrop("scale")).generate("car_lady_dog.jpg")!, cloudinary: cloudinary)
Client Hints are currently only supported by the Chrome, Opera and Android browsers, and they require adding a meta header to your HTML page for enabling their usage. For other browsers, you may want to specify a fallback width value to resize the image.
See a live sample page of Automatic Responsive Images With Client Hints and the Automatic responsive images documentation page for more details.
We believe that the four image automation capabilities that were introduced here are extremely useful for any web developer. From my personal experience, once you start using the new features, you will probably find it hard to recall what you did beforehand.
More than 120,000 developers have signed up to Cloudinary and over 3,000 of them are active paying customers. Our vision is to answer all their image (and video) related needs, and we believe that the new automatic algorithms are a big step towards realizing this vision.
Automatic algorithms, as intelligent as they can be, are not always perfect and the quality of the result might be subjective. Therefore, Cloudinary supports a manual override via the API or the UI: override automatic cropping by specifying custom coordinates for the image, and override the automatically selected quality level by specifying a custom level. The manual overriding actions are also used as feedback for our algorithms, allowing us to further enhance and fine tune the algorithms until as many images as possible are solved.
In our upcoming posts, we’ll share more in-depth details about each of our new automation capabilities. Stay tuned, and as always, we would appreciate your feedback. Try it out?
All image manipulation and delivery features introduced here are now available with no extra charge for all Cloudinary's plans , including the free plan.
Notes:
- The responsive breakpoints support (
w_auto:breakpoints
) currently requires a custom domain name (CNAME) or a private CDN distribution, which are available for Cloudinary's Advanced plan and above. This feature might also involve certain additional costs.
- For customers with a custom domain name or private CDN distribution, the features that involve Client-Hints processing (
w_auto
and q_auto
with Save-Data
support) need to be set up. Contact us for more details. UPDATE - September 2016: features that involve Client-Hints processing are now available for all customers without the need for a specific setup.