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 v1:
Copy to clipboard
cl_image_tag("chess/rnbqkbnr/ppp1pppp/8/3p4/2PP4/8/PP2PPPP/RNBQKBNR b KQkq c3 0 2")
PHP v2:
Copy to clipboard
(new ImageTag('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 v1:
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")
  )))
PHP v2:
Copy to clipboard
(new ImageTag('chess/rnbqkbnr/ppp1pppp/8/3p4/2PP4/8/PP2PPPP/RNBQKBNR b KQkq c3 0 2'))
  ->addVariable(Variable::set('img', Expression::expression('current')))
  ->addVariable(Variable::set('ws', 600))
  ->resize(Resize::scale()->width(50))
  ->overlay(
      Overlay::source(Source::image('web_assets/chess_background')
        ->transformation((new ImageTransformation())
          ->resize(Resize::fill()->width('$ws')->height('$ws')->gravity(Gravity::compass(Compass::center())))
          ->effect(Effect::blur()->strength(800)))))
    ->overlay(
        Overlay::source(Source::image('$img')
          ->transformation((new ImageTransformation())
            ->border(Border::solid(10, Color::WHITE))
            ->resize(Resize::scale()->width(400))
      
    )));
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" variables={[["$img", "current"], ["$ws", "600"]]}>
  <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" :variables="[['$img', 'current'], ['$ws', '600']]">
  <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" variables="[['$img', 'current'], ['$ws', '600']]">
  <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

Partner news: Cloudinary-Getty Images Integration

Supported by intelligent automation, Cloudinary serves as an effective conduit between media asset management and delivery so you can take maximum advantage of assets, compress workflows, and build and coordinate engaging and inspiring customer experiences. Through Cloudinary’s Digital Asset Management (DAM) solution, which employs the company’s innovative image and video APIs, creative and marketing teams can benefit from them, as well as from many AI-powered and automated capabilities. As a result, you can transform, optimize, and deliver media at scale on an intuitive UI.

Read more
Why Audio in Video Matters

Many content creators and consumers tend to regard video as visuals, but that’s only part of the experience. Immersive video content includes strong audio. Just like in a movie, the audio for video content comprises many components: the narrator or subjects, the background music that sets the mood and draws viewers in, sound effects, and so forth.

Read more

For Developers: the HTML <picture> Element Explained

By Amarachi Amaechi
For Developers: the HTML <picture> Element Explained

We all know the good ol', tireless <img> element, which has been a long-time go-to for inserting graphics into webpages. Time doesn’t stop, however, and neither do technological advancements. So, let’s get you up to speed with the element’s modern alternative: the <picture> element.

Read more