Creating a Robort JSON Dashboard

Any of the clickable links in this guide shows images or GIFs that demonstrate whatever is being discussed. If you're confused, check them out!

Adding your JSON to configuration

In the future, you'll be able to add and work with new dashboards in Robort without touching the code. For now, you'll need to make dashboards selectable by modifying the configuration drop down directly. Go to Config_tab.html to the section of the code that looks like this (corresponding to the dashboards drop down):

<div class="form-group">
                                <label for="compOption">What competition season would you like to use?</label>
                                <select id="compOption" class="form-control bg-dark">
                                    <option value="Reefscape">2025 - Reefscape</option>
                                    <option value="Crescendo">2024 - Crescendo</option>
                                    <option value="InfiniteRecharge">2020 - Infinite Recharge (LEGACY)</option>    
                                </select>
                            </div>

Under the latest <option value=...></option> and before </select>, you'll add a configuration option for the dashboard you're creating.

<option value="competition_name">[year] - Competition</option>

Replace "competition_name" with the NAME OF THE DASHBOARD JSON FILE. This is created in the next step; look a bit ahead if you're not sure what this name should be. If it is not exactly the same, your config option WILL NOT WORK. Replace "[year]" with the year the competition took place. Replace "Competition" with the name of the Competition.

As far as testing / viewing your changes is considered, you should be using scoutingUI.html. This is where the main interface will appear.

In order to switch to the dashboard you've just created, open scoutingUI.html in your browser and click the home page button, then go to Configure and open the 'Competition Settings' dropdown. Click on the one corresponding to your new dashboard, save, then open Scouting -> Scouting UI in the navbar to get back to where you started.


Creating your dashboard file

Make a new JSON file in the dashboard directory (it has Crescendo.json, Reefscape.json, etc). It should be titled [what you replaced "competition_name" with earlier].json For example, if I replaced "competition_name" with beep, my file should be called beep.json. (From here on out, I'll be working under the assumption that my competition name is "beep".)

Copy this entire block of code into your file. We'll walk through what to do to make changes step by step next.

"fieldImage": "images/.png",

This line controls the background image you see on the field dashboard. In order for this to display properly, you should upload a __1536x664px size image of the field to the images directory. This should be as clean as possible and can usually be found via the game manual, Chief Delphi, etc. Example here. You might need to do some resizing or tweaking as well. This should be uploaded under naming convention "[competition]field.png"

buttonPositions

Broadly, this codeblock controls every button you will ever use in a Robort dashboard. The "field" codeblock controls all the buttons mapped to the field (robot positions, driver station locations, game piece buttons), and the "positions" codeblock controls all the position buttons (which show where a robot is starting on the field). Don't get them confused- buttonPositions =/= position buttons!

"buttonPositions": {
    "field": {
      "positions": {
        "position1": {
          "id": "position-btn-p1",...
        },
        "position2": {
          "id": "position-btn-p2",...
        },...
        "position8": {
          "id": "position-btn-p8",...
        }
      }

Don't change any of the code that looks like this. YOU ALSO CANNOT REMOVE ANY POSITION CONTAINERS. You're only responsible for four attributes: top, left, name, and hide. This is what a position container with the maximum possible attributes could look like:

"position1": {
          "id": "position-btn-p1",
          "top": "30%",
          "left": "50%",
          "name": "Position 3",
          "hide": false;
        },...

Let's break this down line by line.

The initial "position1" and id attribute should NEVER change. Leave these the way they are in the template.

Think of the top and left attributes as a block on top or to the left of an element on the page. The greater a top percentage, the larger the block on top, meaning the element will get pushed down. The greater a left percentage, the larger the block to the left, meaning to the element will get pushed right. The same logic applies for decreasing percentages.

The name attribute is redundant (meaning you don't need to include it) unless you have fewer than EIGHT places (four per alliance side) where robots can begin the match.

If you do have fewer, you will need to use the name attribute in tandem with the hide attribute. As of 12/1/2025, the position buttons are hardcoded- the first four elements always correspond to the blue alliance, while the last four elements always correspond to the red alliance. This means if you want, say, three positions per alliance, you would need to hide Position 4 and Position 8 ("hide": true") and name Position 5, 6, and 7 to Position 4, 5, and 6 respectively ("name": "Position 4", etc).

(Note: "hide": false is redundant. If you're showing a button, it's already visible, and you don't need to call the attribute.)

driverStations

"driverStations": {
        "driverStation1_BLUE": {
          "id": "driver-station-btn-1",
          "top": "%",
          "left": "%"
        },
        "driverStation2_BLUE": {
          "id": "driver-station-btn-2",
          "top": "%",
          "left": "%"
        },
        "driverStation3_BLUE": {
          "id": "driver-station-btn-3",
          "top": "%",
          "left": "%"
        },
        "driverStation1_RED": {
          "id": "driver-station-btn-4",
          "top": "%",
          "left": "%"
        },
        "driverStation2_RED": {
          "id": "driver-station-btn-5",
          "top": "%",
          "left": "%"
        },
        "driverStation3_RED": {
          "id": "driver-station-btn-6",
          "top": "%",
          "left": "%"
        }
      },

The initial "driverStation[num]_[alliance]" and id attribute should NEVER change. Leave these the way they are in the template.

The only attributes you can control for these are top and left. Think of the top and left attributes as a block on top or to the left of an element on the page. The greater a top percentage, the larger the block on top, meaning the element will get pushed down. The greater a left percentage, the larger the block to the left, meaning to the element will get pushed right. The same logic applies for decreasing percentages.

notes

"notes": {
        "notebtn1": {
          "id": "notebtn1",
          "top": "%",
          "left": "%",
          "name": ""
        },
        "notebtn2": {
          "id": "notebtn2",
          "top": "%",
          "left": "%",
          "name": ""
        },
        "notebtn3": {
          "id": "notebtn3",
          "top": "%",
          "left": "%",
          "name": ""
        },
        "notebtn4": {
          "id": "notebtn4",
          "top": "%",
          "left": "%",
          "name": ""
        },
        "notebtn5": {
          "id": "notebtn5",
          "top": "%",
          "left": "%",
          "name": ""
        },
        "notebtn6": {
          "id": "notebtn6",
          "top": "%",
          "left": "%",
          "name": ""
        },
        "notebtn7": {
          "id": "notebtn7",
          "top": "%",
          "left": "%",
          "name": ""
        },
        "notebtn8": {
          "id": "notebtn8",
          "top": "%",
          "left": "%",
          "name": ""
        },
        "notebtn9": {
          "id": "notebtn9",
          "top": "%",
          "left": "%",
          "name": ""
        },
        "notebtn10": {
          "id": "notebtn10",
          "top": "%",
          "left": "%",
          "name": ""
        },
        "notebtn11": {
          "id": "",
          "top": "%",
          "left": "%",
          "name": ""
        }
      }
    },

"Notes?? What could notes possibly be??" you ask...

They're game pieces laid out at the start of a match. We built Robort for the Crescendo game first and foremost (which used notes as game pieces), and this is a hardcoded remnant. For now, you'll just have to live with it. Sorry!

Please note that you may have to get creative with the note placement. Sometimes there are more than eleven game pieces on the field, and we currently only have 11 buttons to use! You might consider using arrow emojis or something of that nature to make a button for a general area of game pieces, rather than one specific game piece. It's up to you!

The initial "notebtn[num]" and id attribute should NEVER change. Leave these the way they are in the template.

Think of the top and left attributes as a block on top or to the left of an element on the page. The greater a top percentage, the larger the block on top, meaning the element will get pushed down. The greater a left percentage, the larger the block to the left, meaning to the element will get pushed right. The same logic applies for decreasing percentages.

The name attribute is should be customized according to the name/function of the game piece AND accommodate for redundancies when you have fewer than 11 game pieces. Their default names are "Note 1, Note 2... Note 11". If you do have fewer, you will need to use the name attribute in tandem with the hide attribute, as all the game piece elements are hardcoded.

(Note: "hide": false is redundant. If you're showing a button, it's already visible, and you don't need to call the attribute.)

teleop

Broadly-we are still working within buttonPositions-this codeblock AND additionalActions (below) controls every button you will ever use in the TELEOP PHASE of a match.

"teleop": {
      "": {
        "id": "",
        "top": "px",
        "left": "%",
        "width": "px",
        "height": "px",
        "name": "",
        "color": "",
        "size": "",
        "actionType": "",
        "customAttribute": true
      }
    }

This block of code

      "": {
        "id": "",
        "top": "px",
        "left": "%",
        "width": "px",
        "height": "px",
        "name": "",
        "color": "",
        "size": "",
        "actionType": "",
        "customAttribute": true
      }

represents a generic teleop BUTTON and can be copied as many times as necessary. All you need to do is add a comma after the final closing bracket every time you add a new button (the final closing bracket should not have a comma).

"": {
        "id": "",

Unlike the auto buttons, you can give a teleop button any ID you want. The only thing you need to be aware of is that the name in "" should be the same as the name in "id": "".

top must be written with unit px (pixels) (e.g. 300px) and left must be written with unit %.

Think of the top and left attributes as a block on top or to the left of an element on the page. The greater a top # of pixels, the larger the block on top, meaning the element will get pushed down. The greater a left percentage, the larger the block to the left, meaning to the element will get pushed right. The same logic applies for decreasing percentages.

name should be the name of the button, according to its function (e.g. if you want users to press the button when [Game Piece] is scored in [Structure], you would call it Game Piece [Structure]).

Don't get ahead of yourself here! We have sub-actions that you can make as well, so you should only account for the broadest possible actions at this point in the code. For example, if I can score in level 1, 2, and 3 in [Structure], I would still call the button "Game Piece [Structure]". We'll worry about sub-actions in a little bit.

The color attribute takes named colors, hex codes, RGB/RGBA, and HSL/HSLA.

As of 12/1/2025, the size attribute is a little bit hardcoded and corresponds to how large your button text is. If you want it a little larger, type "large"; anything other entry will default to a "small" font size.

actionType should be set to "score" if there's really only one pathway for scoring (let's say there's a hole without levels that, when scored in, grants points immediately), and "pickup" if there are multiple (let's say level 1, level 2, level 3, level 4).

This is where additionalActions come in... (also, don't touch " "customAttribute": true" ")

additionalActions / endgame

"additionalActions": {
      "": {
        "id": "",
        "top": "px",
        "left": "%",
        "width": "px",
        "height": "px",
        "name": "",
        "color": "",
        "size": ""
      }
    },
    "endgame": {
      "": {
        "id": "",
        "top": "px",
        "left": "%",
        "width": "px",
        "height": "px",
        "name": "",
        "color": "",
        "size": ""
      },
      "": {
        "id": "",
        "top": "px",
        "left": "%",
        "width": "px",
        "height": "px",
        "name": "",
        "color": "",
        "size": ""
      },
      "": {
        "id": "",
        "top": "px",
        "left": "%",
        "width": "px",
        "height": "px",
        "name": "",
        "color": "",
        "size": ""
      }
    },

Here's the generic block of code to copy when you want to make a new additional action or endgame button (refer to the teleop generic block for more details):

"": {
        "id": "",
        "top": "px",
        "left": "%",
        "width": "px",
        "height": "px",
        "name": "",
        "color": "",
        "size": ""
      }

additionalActions controls the buttons that appear when you click on a "pickup" actionType button.

endgame controls the buttons that appear in the endgame phase of the match. You will want to do your best to either A) line these up with the actual endgame structures on the field (meaning smaller, centered buttons) B) OR style them like the teleop buttons, large and off to the side.

Try to color them based on the corresponding alliance. Naming should be pretty straightforward as well; for example, here's an example from the Crescendo game: "Stage Left RED, Center Stage RED, Stage Right RED, Stage Left BLUE..."

You don't need to simplify TOO MUCH- assume the person reading has a basic understanding of the game and its features.

Creating these works exactly the same as teleop, so look to that if you're not sure how the attributes here work. The only difference is there's no actionType and customAttribute attributes.

endgameOutcome

 "endgameOutcome": {
      "": {
        "id": "",
        "name": "",
        "top": "px",
        "left": "px",
        "width": "px",
        "height": "px",
        "color": "",
        "size": ""
      },
      "": {
        "id": "",
        "name": "",
        "top": "px",
        "left": "px",
        "width": "px",
        "height": "px",
        "color": "",
        "size": ""
      },
      "": {
        "id": "",
        "name": "",
        "top": "px",
        "left": "px",
        "width": "px",
        "height": "px",
        "color": "",
        "size": ""
      }
    },

Same logic here as additionalActions, except for endgame instead of teleop. Any action that would follow getting to an endgame spot should be added here.

Here's the generic block of code to copy when you want to make a new endgame outcome button (refer to the teleop generic block for more details):

"": {
        "id": "",
        "top": "px",
        "left": "%",
        "width": "px",
        "height": "px",
        "name": "",
        "color": "",
        "size": ""
      }

Don't touch any of this:

 "addtop": -10,
    "jsonAttributes": {
      "matchNumber": "matchNumber",
      "teamNumber": "teamNumber",
      "scoutingData": "scoutingData",
      "teleopActions": "teleopActions",
      "endgameActions": "endgameActions",
      "notes": "notes"
    }

And you're done! Wowza! You should've created a complete dashboard at this point! Contact @teebeeYT on Discord if you have any questions :)