How I turned my PS4 controller into a keyboard

Our developer Tommy Svensson has been trying to find the most comfortable way to code during Home Office.
Feb 15, 2021 · 7 min read
Photo by Alvaro Reyes on Unsplash

Since going fully remote because of the virus I’ve spent countless hours trying to find the most optimal way to sit in front of the computer. I’ve tried it all. Sitting on the floor, in the sofa, lying in bed. The one thing, all of these not-at-the-desk solutions have in common, is this: the keyboard and mouse make them unergonomic and uncomfortable.

This is not a new idea I have. Imagine being able to lounge back in the sofa with a PS4 controller while at work? Sure, there are chorded keyboards out there and they may be more optimized than my DIY project. But if you wanna save your cents and use something you already have at home, look no further; I have just the solution for you!

Initially I tried creating an Electron app, but had some major issues getting the libraries to work with each other and I didn’t find the framework to be very straightforward. Then I remembered Go, the language I’ve been learning for the past two years on and off. What better language to do a desktop application in than Go!

Want to recreate what I did?

Let’s start off by making a simple “Hello world” application. I assume you’ve already installed Go. Create a file main.goand write the following:

package main

Run by typing go run main.go in the terminal.

Next, we will need the Gobot framework to be able to read the controller inputs. The package requires SDL2, so let’s install it with:

brew install sdl2

Once that’s done, install Gobot with the following command:

go get -d -u

Good, now we need to set up and connect our adaptor and driver. We add in some listeners for two of the buttons in a function called workand then we make a robot by connecting it all together and finally run the Start()method on our robot.

package main

If you now plug in your PlayStation DualShock 4 controller, run go run main.goand press either square or triangle. You should receive some messages saying if you are pressing or releasing the button.

Next step is to control your keyboard with Go code. We will do this using Robotgo. Get the package with the command:

go get

Add it to your imports:

import (

Now, all we need to translate a button-press on your PS4 controller into a keyboard key-press, is this:

stick.On(joystick.SquarePress, func(data interface{}) {

Run the app and press square and you should see the letter abeing written in the command line. For macOS users, you may have to go into your privacy settings/accessibility and allow the terminal to control your computer.

There is also another method robotgo.TypeStr(). If you use this, you can put in full strings and it will command your computer to write these out, which will come in handy later as you will see. I've actually chosen to use this method for everything except keys like shift, alt, enter, etc. The reason being, that if I wanna use shift to make uppercase letters, I need to transform the original string with the letter into uppercase using strings.toUpper(). And since the akey is always lowercase, robotgo.KeyTap("A")won't work.

Let’s make our app a little more sophisticated. We want to be able to use the L2 and R2 triggers on our PS4 controller as modifiers so that we can write different letters depending on which trigger is held. We also want to use our L1 trigger as shiftso that we can write in uppercase.

We’ll start by creating a Modsstruct above the main function. It contains bools, that will determine, if the triggers are held down or not. We'll call L1 capssince that will be its only functionality.

type Mods struct {
l2 bool
r2 bool
caps bool

Next, we add a OutputKeyboardmethod on it, that checks, which mods are held down, and gives the appropriate key.

First, import the strings package:

import (

Create a new reference to it in a variable inside main():

mods := &Mods{false, false, false}

Next we’ll listen to trigger presses and releases to set the mods to true or false. Add to the workfunction inside main():

stick.On(joystick.L2Press, func(data interface{}) {
mods.l2 = true
stick.On(joystick.L2Release, func(data interface{}) {
mods.l2 = false
stick.On(joystick.R2Press, func(data interface{}) {
mods.r2 = true
stick.On(joystick.R2Release, func(data interface{}) {
mods.r2 = false
stick.On(joystick.L1Press, func(data interface{}) {
mods.caps = true
stick.On(joystick.L1Release, func(data interface{}) {
mods.caps = false

Finally we react to the button releases themselves. Replace what you have in the workfunction with this:

stick.On(joystick.XRelease, func(data interface{}) {
mods.OutputKeyboard("e", "i", "a")
stick.On(joystick.SquareRelease, func(data interface{}) {
mods.TypeString("f", "j", "b")
stick.On(joystick.TriangleRelease, func(data interface{}) {
mods.TypeString("g", "k", "c")
stick.On(joystick.CircleRelease, func(data interface{}) {
mods.TypeString("h", "l", "d")

Now you should be able to write the letter “a” through l on your PS4 controller. The finished code should look like below. Happy typing :)

package main

(NOTE: At the time of this writing, at least my DualShock 4 controller’s buttons do not correspond with the methods of Gobot’s joystick package. I have sent in a pull request to Gobot to fix this, but it might not be fixed when you read this article. If not, you will have to do some trial and error and find out which button corresponds to which joystick method.)

Choosing your buttons

Choosing which buttons should correspond to which letters was anything but easy, and as I’m writing out this sentence on my gamepad, I look back at the many times I scrapped the bindings I had and had to relearn what little motor memory I had gathered.

My first version made everything much easier to remember, but it also made it very hard to type fast. It scrolled through the alphabet as you held down buttons. Another downside was that you had to look at the terminal to see which letter you were currently on.

Another method I tried was having helper words with unique letters laid out at different parts of the gamepad, but that didn’t make me really memorize the letters. I just memorized the words themselves.

I finally landed on making the most frequent letters the most accessible (meaning no mods or buttons that are hard to reach) and then simply lay them out on random buttons. The plan is to first learn a few really well, then adding a few more and keep going until I’ve learned the whole alphabet. To determine which letters are most common in English text, I used a list made by Samuel Morse (1791–1982) that he used to make Morse Code.

I’m still very slow but I’m getting there. Had to write my own little quiz program to test my skills:

My next idea is to have different button combinations generate certain keywords that pop up frequently in JavaScript, such as:

const, let, function, () =>, return, map, forEach, filter, reduce, some, any, find, findIndex

My hope is that once I get over the hurdle of writing out words with the gamepad, these types of commands will make me work even faster than I could on a keyboard. The sky’s the limit!

To follow my progress and get some ideas of button combinations that can be used, please check out my project on Github:

Tommy is one of our most versatile engineers, spanning both frontend and backend development, always dabbling with new tech and new programming languages. is a studio for strategy, design and code in Berlin, featuring a multidisciplinary team of designers, developers and strategists. We create tailor-made digital solutions with an agile mindset and a smile on our faces. Let’s work together!

Curious to learn more? We’re also on Twitter, Facebook, Instagram and we highly recommend our Tumblr. You could also subscribe to this publication to get notified when new posts are published! That’s all. Over & out! 🛰

Thoughts, observations and learnings from Berlin-based…