Generate Images from JSON Using Photoshop


I have recently been working on a board game that requires hundred of cards. Each card has images, icons, numbers, text values, and other content. Some cards have a top left background, others have text in the middle, or an icon in the corner.

In order to create the game I needed to create not just these hundreds of cards, but I need to iteratively change them in order to fine tune and dial in the game. Some cards had attack values, produced various forms of resources, and had other game effects. I needed to be able to quickly update these values on lots of cards, without manually changing dozens or hundreds of photoshop files each time. Being a software engineer, this was a challenge that I had the skillset to take head on.

The first step was to automate the photoshopping process using scripting. Photoshop allows you to run JavaScript scripts that can essentially do everything you could do manually, but through code. This can be quite powerful, but also quite complex. The folders and layers of the photoshop file need to match what the script expects.

The second step was to control the actions of that script through an easy to update structured date format. For this I used JSON, which is a data format that contains strings of text, numbers, booleans, arrays, and so on. This JSON was essentially a list of items, where each item contained the data needed to generate a card. This data would have text for things like titles and descriptions, numbers for things like combat values, and toggles for determining which icons to show and where to show them.

This two step process is conceptually easy, meaning that you can basically feed the script JSON and it outputs ready to print cards in JPEG format. However, in order to get this concept to be powerful and effective, the devil is in the details.

While I cannot go into detail on every problem and how I solved it, I wanted to highlight some of the key epiphanies I had while trying to make this work.

The Photoshop file needed structured in a precise way in order for the script to be capable of easily manipulating that file. To do this, I created three top level folders: symbols, copied_symbols, and elements.

The symbols folder contained all the possible icons that I might want to use. Each one was named something unique and was quite large. I did not flatten these symbol layers, choosing to leave the layer effects in place. The script would then take these large symbols and copy them into the copied_symbols folder. Then it would flatten, resize, and move it to the correct place.

The elements folder contained backgrounds, location references, and text areas. The script would hide and show the backgrounds based on toggle values in the JSON. Other values in the JSON would determine which icon should be used in a certain location in the image. The location references were placeholder icons that would specify where an icon should appear and how large it would be. The script would use these location references to find the size and location of the copied symbols.

The script could also be given more intelligence. For example, it can check the number of characters in the text and resize the font accordingly, or reduce the width of the text box if there is also an icon needed in the same area, and so on.

In time I may write follow up blog posts where I can go into more detail on how to do all of this, and might even create a JS library for creating your own "JSON to Image" software. For now, I would like to give an example of what I was able to accomplish with these tools. Below you will see some JSON. This JSON was given to the script, which then outputted the image below. I could then print this image and cut out the cards for use in my board game prototype.

[
   {
      "cardType":"city",
      "title":"Rome",
      "subTitle":"Rome - Starting City",
      "placement":"city_placement",
      "image":"rome",
      "print":true,
      "combat":{
         "defense":20
      },
      "mod":[
         "wealth",
         "food"
      ],
      "ability":"While recruiting, draw an additional foot troop.",
      "abilitySymbol":"start",
      "desc":"Rome, the Eternal City. Might of the Great Empire. The seat of emperors and popes."
   }, {
      "cardType":"mounted",
      "title":"Knight",
      "placement":"army_placement",
      "image":"horses",
      "print":true,
      "combat":{
         "init":3,
         "attack":4,
         "attackType":"melee",
         "defense":6
      },
      "cost":[
         {
            "costType":"wealth",
            "costVal":2
         },
         {
            "costType":"iron",
            "costVal":2
         }
      ]
   }, {
      "cardType":"civic_building",
      "title":"University",
      "placement":"building_placement",
      "image":"carved-entry",
      "print":true,
      "cost":[
         {
            "costType":"wood",
            "costVal":1
         },
         {
            "costType":"iron",
            "costVal":1
         }
      ],
      "smallBottomMod":"Gather phase: Draw 1 from your research discard pile",
      "mod":[
         "knowledge"
      ]
   }, {
      "cardType":"wonder",
      "title":"Hagia Sophia",
      "subTitle":"Wonder",
      "placement":"building_placement",
      "image":"mystic",
      "print":true,
      "vps":4,
      "mod":"In the gather phase exhaust to search your research deck for one card",
      "cost":[
         {
            "costType":"wealth",
            "costVal":6
         }
      ]
   }
]


Comments

Popular Posts