Disclaimer: For anyone wondering, this post was not sponsored by Cloudinary or is in any way officially affiliated, endorsed, or supported by them. I am just a big fan of them, and enjoy seeing how I can use all their features in unusual combinations.
Background Info:
This task, overlaying solid shapes on Cloudinary, is simple (somewhat), yet might be missed by many as even being possible. It uses a concept that I am fascinated by, that what is displayed on our computer screens is not a single image, but the result of many transformations and calculations. For example, it is impossible (in practical methods) to display a true curved line (or anything other than a perfectly horizontal or vertical line) on a pixel based display; pixels are squares, so the best you can do is push more and more pixels together until, when viewed from a distance, squares that are placed in a non-linear (e.g. polynomial) arrangement start to appear curved. This is why curves in really old video games, that are low resolution, look blocky and jagged.
For those interested, the process of transforming non-pixel-based shapes, such as a vector representation of a circle, into a pixel-based form (which is what you are looking at right now), is called “rasterisation“.
Anyway, I digress. The reason why I brought up transformations is because they are part of the core of Cloudinary, and what allows me to overlay custom shapes on top of images. The fact that Cloudinary lets users “chain” transformations together in an unlimited fashion, means that almost any shape can be dynamically rendered with a Cloudinary URL, given the right combination of pixel transformations.
Note:
Throughout this article, ACCOUNT_NAME stands in for the name of your Cloudinary cloud account, which should be a short string of characters and numbers.
Adding Squares:
Here is a practical example: A simple blue square added to the default Cloudinary sample image. Here is the sample image without any transformations, and the URL used to fetch it.
https://res.cloudinary.com/ACCOUNT_NAME/image/upload/sample
And here is the same image, with a blue square rendered over it:
https://res.cloudinary.com/ACCOUNT_NAME/image/upload/l_pixel,w_200,h_200/co_rgb:4a90e2,e_colorize,fl_layer_apply,g_north_west,x_270,y_100/sample
The trick here is that “l_pixel” maps to an image I’ve uploaded to my account, with the public ID of “pixel”, and is a 1×1 solid colored pixel saved as an image file. The transformations applied above blow it up to 200 x 200 pixels, position it at x=270 and y=100, and “colorize” it with an RGB / Hex string corresponding to blue. You might think this is very basic, but the fact that you can chain these together means you can do crazy things like this:
https://res.cloudinary.com/ACCOUNT_NAME/image/upload/c_fill,w_500,h_500,o_40/h_302,l_pixel,w_17,x_146,y_48/co_rgb:4a90e2,e_colorize,fl_layer_apply,g_north_west,x_146,y_48/h_21,l_pixel,w_100,x_63,y_332/co_rgb:4a90e2,e_colorize,fl_layer_apply,g_north_west,x_63,y_332/h_97,l_pixel,w_20,x_63,y_254/co_rgb:4a90e2,e_colorize,fl_layer_apply,g_north_west,x_63,y_254/h_20,l_pixel,w_153,x_70,y_47/co_rgb:4a90e2,e_colorize,fl_layer_apply,g_north_west,x_70,y_47/h_100,l_pixel,w_100,x_166,y_70/co_rgb:000000,e_colorize,fl_layer_apply,g_north_west,x_166,y_70/h_15,l_pixel,w_85,x_246,y_50/co_rgb:6283aa,e_colorize,fl_layer_apply,g_north_west,x_246,y_50/h_83,l_pixel,w_15,x_246,y_51/co_rgb:6283aa,e_colorize,fl_layer_apply,g_north_west,x_246,y_51/h_20,l_pixel,w_89,x_246,y_117/co_rgb:6283aa,e_colorize,fl_layer_apply,g_north_west,x_246,y_117/h_76,l_pixel,w_17,x_321,y_117/co_rgb:6283aa,e_colorize,fl_layer_apply,g_north_west,x_321,y_117/h_22,l_pixel,w_100,x_240,y_173/co_rgb:6283aa,e_colorize,fl_layer_apply,g_north_west,x_240,y_173/h_138,l_pixel,w_19,x_327,y_173/co_rgb:27a456,e_colorize,fl_layer_apply,g_north_west,x_327,y_173/h_21,l_pixel,w_79,x_331,y_235/co_rgb:27a456,e_colorize,fl_layer_apply,g_north_west,x_331,y_235/h_100,l_pixel,w_23,x_386,y_238/co_rgb:27a456,e_colorize,fl_layer_apply,g_north_west,x_386,y_238/sample
Adding Circles:
Again, this is a bit of a “cheat”; there is not built-in way to overlay a solid circle with Cloudinary. However… it is possible to create a circle by first creating a square, and then rounding the corners with a radius. Check it out!
https://res.cloudinary.com/ACCOUNT_NAME/image/upload/c_fill,h_500,w_500/g_north_west,l_pixel,r_150,w_300,h_300/b_rgb:4a90e2,co_rgb:4a90e2,e_colorize,fl_layer_apply,g_north_west,x_90,y_90/sample
Let’s Kick It Up a Notch:
OK, so far this might not seem all that impressive. What is the point? Well, think about how this could be applied to situations where you need to dynamically generate an image overlay based on a set of criteria. For example, if you make an online game and need to generate an achievement badge that can be shared to Facebook as an image, but the colors are based on the user’s profile settings. You can generate that image with Cloudinary, just by passing transformations in the image path, which means the image URL can even be generated completely client-side. Check out this doozy of a URL:
https://res.cloudinary.com/ACCOUNT_NAME/image/upload/c_fill,h_500,o_40,w_500/h_224,l_pixel,w_224/co_rgb:070707,e_colorize,fl_layer_apply,g_north_west,x_28,y_75/h_395,l_pixel,w_454/co_rgb:2744a6,e_colorize,fl_layer_apply,g_north_west,x_23,y_35/h_210,l_pixel,w_210/co_rgb:ffffff,e_colorize,fl_layer_apply,g_north_west,x_34,y_82/c_scale,h_188,l_profilepicture,w_215/fl_layer_apply,g_north_west,x_34,y_107/h_209,l_pixel,w_208/co_rgb:ffffff,e_colorize,fl_layer_apply,g_north_west,x_254,y_82/l_text:Roboto_37_normal:Stats:/co_rgb:000000,e_colorize,fl_layer_apply,g_north_west,x_316,y_93/l_text:Roboto_19_normal:Flowers%20Pollinated%20:%2020/co_rgb:000000,e_colorize,fl_layer_apply,g_north_west,x_266,y_141/l_text:Roboto_19_normal:People%20Chased%20:%2025/co_rgb:000000,e_colorize,fl_layer_apply,g_north_west,x_266,y_167/l_text:Roboto_55_normal:Buzzy%20McBuzzer/co_rgb:ffffff,e_colorize,fl_layer_apply,g_north_west,x_52,y_314/l_text:Roboto_31_normal:Rank%20:%2010/co_rgb:ffffff,e_colorize,fl_layer_apply,g_north_west,x_170,y_380/h_119,l_pixel,w_119/co_rgb:c8c61f,e_colorize,fl_layer_apply,g_north_west,r_59,x_295,y_194/l_text:Roboto_42_normal:%3C10%3E/co_rgb:000000,e_colorize,fl_layer_apply,g_north_west,x_308,y_237/sample
Crazy, right? However, once you come up with a set of transformations you want to use together, you could either save it as a saved transformation, or dynamically generate the URL with just a little bit of code, for example, like this:
function getGamerBadge(gamerProfile){
return 'https://res.cloudinary.com/ACCOUNT_NAME/image/upload/c_crop,h_500,o_40,w_500/h_224,l_pixel,w_224/co_rgb:000000,e_colorize,fl_layer_apply,g_north_west,x_28,y_75/h_395,l_pixel,w_454/co_rgb:' + gamerProfile.colors.dark + ',e_colorize,fl_layer_apply,g_north_west,x_23,y_35/h_210,l_pixel,w_210/co_rgb:' + gamerProfile.colors.light + ',e_colorize,fl_layer_apply,g_north_west,x_34,y_82/c_scale,h_188,l_' + gamerProfile.profileImageId + ',w_215/fl_layer_apply,g_north_west,x_34,y_107/h_209,l_pixel,w_208/co_rgb:' + gamerProfile.colors.light + ',e_colorize,fl_layer_apply,g_north_west,x_254,y_82/l_text:Roboto_37_normal:Stats:/co_rgb:000000,e_colorize,fl_layer_apply,g_north_west,x_316,y_93/l_text:Roboto_19_normal:Flowers%20Pollinated%20:%20' + gamerProfile.stats.pollinated + '/co_rgb:000000,e_colorize,fl_layer_apply,g_north_west,x_266,y_141/l_text:Roboto_19_normal:People%20Chased%20:%20' + gamerProfile.stats.chased + '/co_rgb:000000,e_colorize,fl_layer_apply,g_north_west,x_266,y_167/l_text:Roboto_55_normal:' + encodeURIComponent(gamerProfile.name) + '/co_rgb:' + gamerProfile.colors.light + ',e_colorize,fl_layer_apply,g_north_west,x_52,y_314/l_text:Roboto_31_normal:Rank%20:%20' + gamerProfile.rank + '/co_rgb:' + gamerProfile.colors.light + ',e_colorize,fl_layer_apply,g_north_west,x_170,y_380/h_119,l_pixel,w_119/co_rgb:' + gamerProfile.colors.badge + ',e_colorize,fl_layer_apply,g_north_west,r_59,x_295,y_194/l_text:Roboto_42_normal:%3C' + gamerProfile.rank + '%3E/co_rgb:000000,e_colorize,fl_layer_apply,g_north_west,x_308,y_237/sample';
}
var buzzyGamerProfile = {
name : 'Buzzy McBuzzer',
rank : 10,
stats : {
pollinated : 20,
chased : 25
},
profileImageId : 'profilepicture',
colors : {
dark : '2744A6',
light : 'ffffff',
badge : 'c8c61f'
}
}
var buzzyGamerBadgeImageSrc = getGamerBadge(buzzyGamerProfile);
More information:
I am currently working on a tool related to all of these features, but in the mean time, here are some additional resources to check out if you are interested in the power-user features of Cloudinary transformations:
- Cloudinary Transformation Reference / Cheat Sheet
- Transformation “Cookbook” – shows examples of different effects and transformation chains, with URL and code examples.
- Cloudinary JavaScript library source code – useful for looking at class definitions of different Cloudinary layers and how they work together. As mentioned in another post, this is where I tracked down a bug I was running into related to remote fetch overlay transformations.