Cloudinary Blog

In Chess As in Customer Engagement: Observe, Strategize, and Learn

Cloudinary's Mapping Capability Creates Images From Forsyth-Edwards Notation Strings

In October, Netflix launched a wonderful miniseries based on Walter Tevis’s 1983 novel, The Queen’s Gambit, on the rise of a young prodigy to be the world's chess champion while tackling issues of emotional attachment and drug addiction stemming from childhood. That miniseries is a smash hit among chess fans. Since its release, Google’s search queries for “chess” have doubled, and those for “how to play chess” have hit a nine-year peak. Inquiries for “chess sets'' on eBay are up a whopping 250 percent1.

Watching The Queen’s Gambit took me back in time to a memorable experience 21 years ago, my senior year in high school. To fulfill part of my final-exam requirement for Computer Science, I wrote a chess program in Pascal—a truly enjoyable and stimulating adventure. The algorithm was based on a calculation of the scores of potential moves, with a tree of nodes holding all the options for the current position and the potential responses for each move from the opponent. The total score for each pair of moves determines what the computer does. Due to memory limitation, the tree holds only two levels of moves: one for the current position and the other for the opponent’s potential moves.

chess on paper

Chess is all about observing, strategizing, experimenting, learning, and building on knowledge, which all came into play in that program I built long ago, a vivid and fond memory to this day. Now, as customer engagement manager at Cloudinary, I approach the task of automating our functional programs as a chess game, that is, look for the best content to present based on customer behavior and strategize the next move according to the reactions and feedback from our patrons. In chess lingo, checkmate in this “game” ensues from customer success and satisfaction with the product.

Now on to an example of a Cloudinary capability that caters to our customers’ media-management needs: mapping and its helpful role in creating an image from a string based on the Forsyth-Edwards Notation (FEN), the standard way of describing positions of a chess game.

As a prelude, let me introduce the Node.js library chess-image-generator (link), which was built by Andrew Young, a brilliant developer who formerly worked on the front end at Chess.com. Not surprisingly, since the release of The Queen’s Gambit, Chess.com has enjoyed a significant surge in the number of new players.

Here’s what to do:

How to

  1. Incorporate chess-image-generator, which accepts FEN strings as URL parameters and then responds with PNG images, into a Google function (code).
  2. Map that function to Cloudinary for transformation of the image followed by delivery through a fast content delivery network (CDN).

Ruby:
Copy to clipboard
cl_image_tag("chess/rnbqkbnr/ppp1pppp/8/3p4/2PP4/8/PP2PPPP/RNBQKBNR b KQkq c3 0 2")
PHP:
Copy to clipboard
cl_image_tag("chess/rnbqkbnr/ppp1pppp/8/3p4/2PP4/8/PP2PPPP/RNBQKBNR b KQkq c3 0 2")
Python:
Copy to clipboard
CloudinaryImage("chess/rnbqkbnr/ppp1pppp/8/3p4/2PP4/8/PP2PPPP/RNBQKBNR b KQkq c3 0 2").image()
Node.js:
Copy to clipboard
cloudinary.image("chess/rnbqkbnr/ppp1pppp/8/3p4/2PP4/8/PP2PPPP/RNBQKBNR b KQkq c3 0 2")
Java:
Copy to clipboard
cloudinary.url().imageTag("chess/rnbqkbnr/ppp1pppp/8/3p4/2PP4/8/PP2PPPP/RNBQKBNR b KQkq c3 0 2");
JS:
Copy to clipboard
cloudinary.imageTag('chess/rnbqkbnr/ppp1pppp/8/3p4/2PP4/8/PP2PPPP/RNBQKBNR b KQkq c3 0 2').toHtml();
jQuery:
Copy to clipboard
$.cloudinary.image("chess/rnbqkbnr/ppp1pppp/8/3p4/2PP4/8/PP2PPPP/RNBQKBNR b KQkq c3 0 2")
React:
Copy to clipboard
<Image publicId="chess/rnbqkbnr/ppp1pppp/8/3p4/2PP4/8/PP2PPPP/RNBQKBNR b KQkq c3 0 2" >

</Image>
Vue.js:
Copy to clipboard
<cld-image publicId="chess/rnbqkbnr/ppp1pppp/8/3p4/2PP4/8/PP2PPPP/RNBQKBNR b KQkq c3 0 2" >

</cld-image>
Angular:
Copy to clipboard
<cl-image public-id="chess/rnbqkbnr/ppp1pppp/8/3p4/2PP4/8/PP2PPPP/RNBQKBNR b KQkq c3 0 2" >

</cl-image>
.Net:
Copy to clipboard
cloudinary.Api.UrlImgUp.BuildImageTag("chess/rnbqkbnr/ppp1pppp/8/3p4/2PP4/8/PP2PPPP/RNBQKBNR b KQkq c3 0 2")
Android:
Copy to clipboard
MediaManager.get().url().generate("chess/rnbqkbnr/ppp1pppp/8/3p4/2PP4/8/PP2PPPP/RNBQKBNR b KQkq c3 0 2");
iOS:
Copy to clipboard
imageView.cldSetImage(cloudinary.createUrl().generate("chess/rnbqkbnr/ppp1pppp/8/3p4/2PP4/8/PP2PPPP/RNBQKBNR b KQkq c3 0 2")!, cloudinary: cloudinary)
Chess Board
Example 1: An image that resulted from the above process

Ruby:
Copy to clipboard
cl_image_tag("chess/rnbqkbnr/ppp1pppp/8/3p4/2PP4/8/PP2PPPP/RNBQKBNR b KQkq c3 0 2", :variables=>[["$img", "current"], ["$ws", "600"]], :transformation=>[
  {:width=>50, :crop=>"scale"},
  {:overlay=>"web_assets:chess_background", :effect=>"blur:800", :width=>"$ws", :height=>"$ws", :gravity=>"center", :crop=>"fill"},
  {:flags=>"layer_apply"},
  {:overlay=>"%24img", :width=>400, :border=>"10px_solid_white"}
  ])
PHP:
Copy to clipboard
cl_image_tag("chess/rnbqkbnr/ppp1pppp/8/3p4/2PP4/8/PP2PPPP/RNBQKBNR b KQkq c3 0 2", array("variables"=>array("$img"=>"current", "$ws"=>"600"), "transformation"=>array(
  array("width"=>50, "crop"=>"scale"),
  array("overlay"=>"web_assets:chess_background", "effect"=>"blur:800", "width"=>"$ws", "height"=>"$ws", "gravity"=>"center", "crop"=>"fill"),
  array("flags"=>"layer_apply"),
  array("overlay"=>"%24img", "width"=>400, "border"=>"10px_solid_white")
  )))
Python:
Copy to clipboard
CloudinaryImage("chess/rnbqkbnr/ppp1pppp/8/3p4/2PP4/8/PP2PPPP/RNBQKBNR b KQkq c3 0 2").image(variables={"$img": "current", "$ws": "600"}, transformation=[
  {'width': 50, 'crop': "scale"},
  {'overlay': "web_assets:chess_background", 'effect': "blur:800", 'width': "$ws", 'height': "$ws", 'gravity': "center", 'crop': "fill"},
  {'flags': "layer_apply"},
  {'overlay': "%24img", 'width': 400, 'border': "10px_solid_white"}
  ])
Node.js:
Copy to clipboard
cloudinary.image("chess/rnbqkbnr/ppp1pppp/8/3p4/2PP4/8/PP2PPPP/RNBQKBNR b KQkq c3 0 2", {variables: [["$img", "current"], ["$ws", "600"]], transformation: [
  {width: 50, crop: "scale"},
  {overlay: "web_assets:chess_background", effect: "blur:800", width: "$ws", height: "$ws", gravity: "center", crop: "fill"},
  {flags: "layer_apply"},
  {overlay: "%24img", width: 400, border: "10px_solid_white"}
  ]})
Java:
Copy to clipboard
cloudinary.url().transformation(new Transformation()
.variables(variable("$img","current"),variable("$ws","600")).chain()
  .width(50).crop("scale").chain()
  .overlay(new Layer().publicId("web_assets:chess_background")).effect("blur:800").width("$ws").height("$ws").gravity("center").crop("fill").chain()
  .flags("layer_apply").chain()
  .overlay(new Layer().publicId("%24img")).width(400).border("10px_solid_white")).imageTag("chess/rnbqkbnr/ppp1pppp/8/3p4/2PP4/8/PP2PPPP/RNBQKBNR b KQkq c3 0 2");
JS:
Copy to clipboard
cloudinary.imageTag('chess/rnbqkbnr/ppp1pppp/8/3p4/2PP4/8/PP2PPPP/RNBQKBNR b KQkq c3 0 2', {variables: [["$img", "current"], ["$ws", "600"]], transformation: [
  {width: 50, crop: "scale"},
  {overlay: new cloudinary.Layer().publicId("web_assets:chess_background"), effect: "blur:800", width: "$ws", height: "$ws", gravity: "center", crop: "fill"},
  {flags: "layer_apply"},
  {overlay: new cloudinary.Layer().publicId("%24img"), width: 400, border: "10px_solid_white"}
  ]}).toHtml();
jQuery:
Copy to clipboard
$.cloudinary.image("chess/rnbqkbnr/ppp1pppp/8/3p4/2PP4/8/PP2PPPP/RNBQKBNR b KQkq c3 0 2", {variables: [["$img", "current"], ["$ws", "600"]], transformation: [
  {width: 50, crop: "scale"},
  {overlay: new cloudinary.Layer().publicId("web_assets:chess_background"), effect: "blur:800", width: "$ws", height: "$ws", gravity: "center", crop: "fill"},
  {flags: "layer_apply"},
  {overlay: new cloudinary.Layer().publicId("%24img"), width: 400, border: "10px_solid_white"}
  ]})
React:
Copy to clipboard
<Image publicId="chess/rnbqkbnr/ppp1pppp/8/3p4/2PP4/8/PP2PPPP/RNBQKBNR b KQkq c3 0 2" >
  <Transformation width="50" crop="scale" />
  <Transformation overlay="web_assets:chess_background" effect="blur:800" width="$ws" height="$ws" gravity="center" crop="fill" />
  <Transformation flags="layer_apply" />
  <Transformation overlay="%24img" width="400" border="10px_solid_white" />
</Image>
Vue.js:
Copy to clipboard
<cld-image publicId="chess/rnbqkbnr/ppp1pppp/8/3p4/2PP4/8/PP2PPPP/RNBQKBNR b KQkq c3 0 2" >
  <cld-transformation width="50" crop="scale" />
  <cld-transformation overlay="web_assets:chess_background" effect="blur:800" width="$ws" height="$ws" gravity="center" crop="fill" />
  <cld-transformation flags="layer_apply" />
  <cld-transformation overlay="%24img" width="400" border="10px_solid_white" />
</cld-image>
Angular:
Copy to clipboard
<cl-image public-id="chess/rnbqkbnr/ppp1pppp/8/3p4/2PP4/8/PP2PPPP/RNBQKBNR b KQkq c3 0 2" >
  <cl-transformation width="50" crop="scale">
  </cl-transformation>
  <cl-transformation overlay="web_assets:chess_background" effect="blur:800" width="$ws" height="$ws" gravity="center" crop="fill">
  </cl-transformation>
  <cl-transformation flags="layer_apply">
  </cl-transformation>
  <cl-transformation overlay="%24img" width="400" border="10px_solid_white">
  </cl-transformation>
</cl-image>
.Net:
Copy to clipboard
cloudinary.Api.UrlImgUp.Transform(new Transformation()
  .Width(50).Crop("scale").Chain()
  .Overlay(new Layer().PublicId("web_assets:chess_background")).Effect("blur:800").Width("$ws").Height("$ws").Gravity("center").Crop("fill").Chain()
  .Flags("layer_apply").Chain()
  .Overlay(new Layer().PublicId("%24img")).Width(400).Border("10px_solid_white")).BuildImageTag("chess/rnbqkbnr/ppp1pppp/8/3p4/2PP4/8/PP2PPPP/RNBQKBNR b KQkq c3 0 2")
Android:
Copy to clipboard
MediaManager.get().url().transformation(new Transformation()
.variables(variable("$img","current"),variable("$ws","600")).chain()
  .width(50).crop("scale").chain()
  .overlay(new Layer().publicId("web_assets:chess_background")).effect("blur:800").width("$ws").height("$ws").gravity("center").crop("fill").chain()
  .flags("layer_apply").chain()
  .overlay(new Layer().publicId("%24img")).width(400).border("10px_solid_white")).generate("chess/rnbqkbnr/ppp1pppp/8/3p4/2PP4/8/PP2PPPP/RNBQKBNR b KQkq c3 0 2");
iOS:
Copy to clipboard
imageView.cldSetImage(cloudinary.createUrl().setTransformation(CLDTransformation()
  .setWidth(50).setCrop("scale").chain()
  .setOverlay("web_assets:chess_background").setEffect("blur:800").setWidth("$ws").setHeight("$ws").setGravity("center").setCrop("fill").chain()
  .setFlags("layer_apply").chain()
  .setOverlay("%24img").setWidth(400).setBorder("10px_solid_white")).generate("chess/rnbqkbnr/ppp1pppp/8/3p4/2PP4/8/PP2PPPP/RNBQKBNR b KQkq c3 0 2")!, cloudinary: cloudinary)
Chess move
Example 2: The same image, post transformation (border+background image)

You can apply this mapping technique to other cases, like parsing an HTML page and obtaining an image from there, or retrieving an image from another storage service or third-party APIs like QR or map generators. Subsequently, the automatic and dynamic process occurs seamlessly in Cloudinary: upload, transformation, optimization, and delivery. It’s reliable, efficient, and secure.

https://about.netflix.com/en/news/the-queens-gambit-netflix-most-watched-scripted-limited-series

Recent Blog Posts

Amplify Your Jamstack With Video

By Alex Patterson
Amplify Your Jamstack With Cloudinary Video

As defined by Amazon Web Services (AWS), Amplify is a set of products and tools with which mobile and front-end web developers can build and deploy AWS-powered, secure, and scalable full-stack apps. Also, you can efficiently configure their back ends, connect them to your app with just a few lines of code, and deploy static web apps in only three steps. Historically, because of their performance issues, managing images and videos is a daunting challenge for developers. Even though you can easily load media to an S3 bucket with AWS Amplify, transforming, compressing, and responsively delivering them is labor intensive and time consuming.

Read more
Cloudinary Helps Move James Hardie’s Experience Online

While COVID has affected most businesses, it has been particularly hard on those that sell products for the physical ‘brick and mortar’ world. One company that literally fits that bill is our Australian customer James Hardie, the largest global manufacturer of fibre cement products used in both domestic and commercial construction. These are materials that its buyers ideally want to see up close, in detail. When customers have questions, they expect personal service.

Read more
How to Build an Enhanced Gravatar Service, Part 2

Part 1 of this post defines the capabilities of an enhanced Gravatar service, which I named Clavatar, and describes the following initial steps for building it:

This post, part 2 of the series, explains how to make Clavatar work like Gravatar and to develop Clavatar’s capabilities of enabling requests for various versions of the images related to user accounts.

Read more
How to Build an Enhanced Gravatar Service, Part 1

The advent of web development since 25 years ago has given birth to an abundance of online services that help developers build apps efficiently and smoothly. Gravatar is a shining example of such a service. Built by WordPress, Gravatar generates globally recognized avatars. Fun fact: 80 percent of Gravatar users first came across that service when reading a WordPress blog.

Read more