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

Enhance Your Travel Site With Cloudinary in Anticipation of a Return to New Normal

Read more
The Benefits of Headless DAMs

Headless is not a buzzword anymore. In fact, the concept of headless architecture is gaining momentum due to the flexibility it offers for composing new experiences and for tackling the undue complexity of an ever-evolving technology stack. That’s because while the evolution of the martech landscape has enabled disruptive, digital innovations, the approach of buying point solutions for solving specific challenges can expose companies to the complicated nature of new technologies, systems, and platforms.

Read more

Building Display Ads With Transparent Video

By Afzaal Ahmad Zeeshan
Build Web Ads With Transparent Video to Attract User Engagement

Billions of views on the Internet every day drive one of the biggest industries on the planet: advertising. The sheer size of that market and the competitive nature of vying for consumer attention results in a constant need for innovation. Readers are jaded, and display ads are blind spots.

Read more
How Cloudinary's Media Optimizer Helps E-Businesses Deliver Superior Web Performance

As a technology company, Cloudinary owes its success to its ability to build solutions that address the most critical challenges you, our customers, face. The companies we serve run the gamut of digital businesses—retailers and direct-to-consumer brands, media and entertainment, travel and hospitality—which, coincidentally, all care about the same things.

Read more