Custom Keybindings in panorama

edited July 2015 in Tutorials


With the recent update (20th of july) valve added support for custom keybindings. That is, you can bind key's to fire a custom command.

The technique used is derived from rpg_example.

Although this method is not nescessarily limited to panorama this tutorial will focus on using them within panorama.


Start by adding a couple of lines to your addoninfo.txt file located in /game/<your addon>/addoninfo.txt

  "TeamCount" "10"
  "maps"      "your_map"
  "IsPlayable"  "1"
          "MaxPlayers"                    "10"
            "Key"       "S"
            "Command"   "CustomGameExecuteAbility1"
            "Name"      "Execute Ability 1"
            "Key"       "Z"
            "Command"   "+CustomGameTestButton"
            "Name"      "Example"

The important parts are of course what is defined in "Default_Keys"

"Key" is the key you want to bind, use capital letters here

"Command" is the command to fire, make sure the command name is unique.
The prefix of the command defines when the command will trigger.

"Name" Name of the command, used for debugging purposes.

Command Prefixes

Prefix Example Description
(nothing) command Command will trigger on press and release
+ +command Trigger when key is pressed (used for normal key press)
- -command Command will trigger when key is released

The prefixes do not lock the command to be triggered only in that event. But is a good self-reference for what you want the keybind to do.


Catching the keybind commands in Panorama is easy:

function OnExecuteAbility1ButtonPressed()
  $.Msg("'S' Pressed or Released");

function OnTestButtonPressed()
  $.Msg("'Z' Pressed");

function OnTestButtonReleased()
  $.Msg("'Z' Released");

(function() {
  Game.AddCommand( "CustomGameExecuteAbility1", OnExecuteAbility1ButtonPressed, "", 0 );
  Game.AddCommand( "+CustomGameTestButton", OnTestButtonPressed, "", 0 );
  Game.AddCommand( "-CustomGameTestButton", OnTestButtonReleased, "", 0 );

Note how the prefixes are used again. Even though we only defined CustomGameTestButton to be fired on key down, we can easily catch the release event in our JS aswell.


  • If someone need to bind Arrows, they are: LEFTARROW, UPARROW, RIGHTARROW, DOWNARROW


        "Key"       "UPARROW"
        "Command"       "UpButton"
        "Name"      "Up"
  • is anybody else not able to capture key release when the command has no prefix?
    not a big problem since i can use the '+' and '-' together, just checking to see if i did something wrong

  • A relatively major side note. Any time the script file, or any script file loosely related to this file is modified and recompiled. This will break. By related I mean other scripts included in the same XML file.

    The solution is to this is:

    In custom_ui_manifest.xml add:

    <CustomUIElement type="Hud" layoutfile="file://{resources}/layout/custom_game/keybinds.xml" />

    In keybinds.xml add:

            <include src="file://{resources}/scripts/custom_game/keybinds.js" />
        <Panel hittest="false"></Panel>

    In keybinds.js add:

    // Even if the code is not re-executed, if a javascript file with a Game.AddCommand
    // is recompiled mid-game, the command completely breaks.
    // They even need their own independent XML file. As just throwing it in as an
    // include in the custom_ui_manifest.xml file will cause it to break.
    // Hence, their relegation to isolation here.
    function WrapFunction(name) {
        return function() {
    Game.AddCommand("+CustomGameCmd", WrapFunction("CustomOnPressed"), "", 0);
    Game.AddCommand("-CustomGameCmd", WrapFunction("CustomOnReleased"), "", 0);

    Then, in some script file that is included prior to that XML file, add:

    Game.CustomOnPressed = function()
        $.Msg('I will survive');
    Game.CustomOnReleased = function()
        $.Msg('I will survive');

    At this point, when you modify your other script file, and it recompiles, the Game.CustomOnPressed function can be modified, without recompiling the keybinds.xml file or javascript file. And thus it won't break.

    Otherwise the moment the javascript file is recompiled, it breaks no matter what you do! Good times.