Cloudinary Blog

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

By
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

Our $2B Valuation

By
Blackstone Growth Invests in Cloudinary

When we started our journey in 2012, we were looking to improve our lives as developers by making it easier for us to handle the arduous tasks of handling images and videos in our code. That initial line of developer code has evolved into a full suite of media experience solutions driven by a mission that gradually revealed itself over the course of the past 10 years: help companies unleash the full potential of their media to create the most engaging visual experiences.

Read more
Direct-to-Consumer E-Commerce Requires Compelling Visual Experiences

When brands like you adopt a direct–to-consumer (DTC) e-commerce approach with no involvement of retailers or marketplaces, you gain direct and timely insight into evolving shopping behaviors. Accordingly, you can accommodate shoppers’ preferences by continually adjusting your product offering and interspersing the shopping journey with moments of excitement and intrigue. Opportunities abound for you to cultivate engaging customer relationships.

Read more
Automatically Translating Videos for an International Audience

No matter your business focus—public service, B2B integration, recruitment—multimedia, in particular video, is remarkably effective in communicating with the audience. Before, making video accessible to diverse viewers involved tasks galore, such as eliciting the service of production studios to manually dub, transcribe, and add subtitles. Those operations were costly and slow, especially for globally destined content.

Read more
Cloudinary Helps Minted Manage Its Image-Generation Pipeline at Scale

Shoppers return time and again to Minted’s global online community of independent artists and designers because they know they can count on unique, statement-making products of the highest quality there. Concurrently, the visual imagery on Minted.com must do justice to the designs into which the creators have poured their hearts and souls. For Minted’s VP of Engineering David Lien, “Because we are a premium brand, we need to ensure that every single one of our product images matches the selected configuration exactly. For example, if you pick an 18x24 art print on blue canvas, we will show that exact combination on the hero images in the PDF.”

Read more
Highlights on ImageCon 2021 and a Preview of ImageCon 2022

New year, same trend! Visual media will continue to play a monumental role in driving online conversions. To keep up with visual-experience trends and best practices, Cloudinary holds an annual conference called ImageCon, a one-of-a-kind event that helps attendees create the most engaging visual experiences possible.

Read more