AutoHotkey for Radiology

AutoHotkey is powerful free software you can use to control your computer and generate simple (or complex) macros to automate tedious or repetitive tasks.

For radiology, I consider the most important ability AHK enables is true hands-free dictation.

Ultimately, you can go crazy with this power, and it only takes a few minutes to learn how to use the software. The AHK website includes beginner tutorials and examples, and ChatGPT is even familiar enough with AHK scripting to get you most of the way. You don’t really need to understand very much in order to use it.

Randall Munroe, author of the always wonderful XKCD and Thing Explainer, illustrates why basically every radiologist should be doing this:

Please note that I am far from an expert on AHK or scripting in general. I started this journey in order to more effectively ditch the dictaphone, and–once I started–realized I also just dislike wasting my time.

I promise: The small investment in time and energy is absolutely worth it.

Context, Location, & Deployment

You create AutoHotkey scripts in a simple text file like Notepad (with the extension .ahk), and you launch the scripts by double-clicking the file on any computer with the free AutoHotkey software installed. The easiest way to create a new one is to right-click on the desktop or in a folder, mouse over “New,” and then select “Autohotkey Script.” To edit a script, right-click on it and select “Edit Script” or “Open with” -> Notepad.

In situations where you can’t install the AHK software, you can create a portable executable (.exe) file of a script you’ve already made through the right-click menu by clicking on the script file icon and selecting “Compile Script”). This means that if you can get that .exe file onto a work computer that generally doesn’t allow for software installation (via USB key, email, OneDrive, shared folder, etc.), you can often still run your personal productivity suite. Of course, begging IT is always an option, it will just take more doing. Note: You can’t edit the .exe files directly, so if you need to make changes, you’ll have to re-compile a new version.

Note that everyone’s combination of PACS, dictation software, and personal preferences is a little different, so it really is worth spending ten minutes to make sure you have the combination of features you want (and ideally the understanding of how to adjust them for troubleshooting).

I will include examples of the macros I use in this post as an illustration of some things you might want to do, explain how they work, and use them to teach you the basics of AHK’s very simple syntax. You can paste these into your own script and adjust if necessary.

At the end of the post, I will include several of the less idiosyncratic functions together into a complete script that you can just copy and paste as a starting point. It works for me in the contexts in which I work, but your mileage may vary. There are probably better ways for smarter people to accomplish similar tasks (remember: not an expert). Again, it’s absolutely worth reading this post (and probably others) to understand at least enough to tweak the hotkeys to your liking.

My context: In my practice, I currently use Powerscribe 360 for dictation, Intelliviewer for outpatient imaging, and GE for hospital PACS.

Pair AHK with Better Input

Before we get in the weeds, please note that while you can create a robust number of macros that do a wide variety of things, the benefits of AutoHotkey for radiology really come alive when you combine AHK triggers with a more powerful/versatile input source like a gaming mouse and/ or a dedicated off-hand device like the Contour Shuttle Pro V2 (my choice), Razer Tartarus V2, or a one-hand macro keypad like the ELSRA Smart or offerings from Koolertron.

For example, this allows me to assign the trigger shortcuts for my macros to otherwise totally unused weird key combinations (e.g. ctrl+q, ctrl+alt+p) and then assign those combinations to a single mouse button or a button on my Shuttle. This leaves my typing experience completely normal and a few other benefits we’ll discuss in a separate post. This is how I control dictation and several other features as well or better than I ever could have with the PowerMic. In practice, it means that I never hold a microphone and rarely need to touch my keyboard when reading routine cases.

Please see the equipment post for more discussion of productivity toy options.

Things You Might Try with AutoHotkey

Let’s illustrate how versatile, powerful, and helpful a few simple scripts can be to your workflow. If you want to just start and stop with hands-free dictation, that’s fine too.

PowerScribe Dictation Controls

In order to ditch the PowerMic and achieve seamless hands-free dictation, we need to be able to control dictation and template field navigation regardless of whether PowerScribe is the currently active application or just running in the background. Without AHK, you’ll need to either use voice controls (e.g. say “next field”) or click on the PowerScribe window first to make it active in order for it to respond to keyboard shortcuts. Neither is ideal.

But we can replicate the dictaphone functionality perfectly by using AHK to trigger the following simple sequence of two events: activate Powescribe, then toggle dictation/switch fields.

For reference, in PowerScribe 360, ‘F4’ toggles dictation, ‘TAB’ moves to the next dictation field, and ‘SHIFT+TAB’ goes to the previous field. The script looks like this:

`::
WinActivate, PowerScribe
Send, {F4}
Return

^`::
WinActivate, PowerScribe
Send, +{Tab}
Return

^1::
WinActivate, PowerScribe
Send, {Tab}
Return

In AHK, you put the trigger in the first line followed by two colons, then each command on its own line, ending the sequence with return. The WinActivate command changes the currently active application (we’ll discuss this more later). Send sends an input. In AHK scripting, the caret (^) means the CTRL key and the plus sign (+) means SHIFT. (!) is for Alt. The comma is optional.

So, in this example, the backtick (`) key toggles dictation, CTRL+` moves to the previous field, and CTRL+1 moves to the next field. I then map those combinations to buttons on my Shuttle. I never actually control these from the keyboard, though you certainly could.

A Pause Wrapper

Before we get into more examples, one thing you might want to do is create a trigger to manually turn the script on and off. This way, if you do use keys or key combinations that are necessary in some situations, you can pause the script. This is very helpful if you plan to use regular keyboard keys instead of a dedicated productivity mouse or off-hand device, or when you want to change the keymapping on one of those devices when the script is already running.

As in: You could set yourself up to turn the script on and off with a rarely used key like the backtick (the one beneath the escape key to the left of 1), just use single keystrokes to control all your favorite macros, and then toggle the script whenever you want to actually type something.

The way I have my script set up is that the top of the script contains the following:

; Set the initial state of the script to active
isActive := true

; Define the hotkey combination to toggle the script
^!p:: ; Ctrl + Alt + P
isActive := !isActive ; Toggle the state
if (isActive)
MsgBox Script is now active.
else
MsgBox Script is now paused.
return

; Optionally, any script code here will always be active

#If (isActive)

; Place all of your actual script code here.

#If

This toggles the script on/off through the shortcut CTRL+ALT+P (P was chosen for “Pause”). In AHK, semicolons precede comments, which are just to make things readable but do not affect functionality.

Powerscrolling

If your PACS requires you to hold the mouse button down to power scroll, that can wreak havoc on your index finger over a long shift. This script maps the backslash to a “click-lock” that toggles between holding the left mouse button down and releasing it.

;toggle holding down the left mouse button
\::
alt := not alt
if (alt)
{
Click Down
}
else
{
Click Up
}
Return

Remove Annoying Double Periods and Double Spaces

One of my favorite functions is cleaning up reports with a series of find-and-replace functions in PowerScribe to remove double spaces between words and double periods. Keeping them in your reports is common but sloppy. Hunting for them and removing them manually is grossly inefficient.

^q::
WinActivate, PowerScribe
Send ^h
Sleep, 100
Send ..
Send {tab}
Send .
Send {tab} {tab} {tab}
Send {Enter}{Escape}{Escape}{Escape}

Send ^h
Sleep, 100
Send {space}{space}
Send {tab}
Send {space}
Send {tab} {tab} {tab}
Send {Enter}{Escape}{Escape}

Send ^h
Sleep, 100
Send {space}.
Send {tab}
Send .
Send {tab} {tab} {tab}
Send {Enter}{Escape}{Escape}{Escape}

return

The Sleep function is a forced delay (in milliseconds); it gives the application a chance to complete the previous command before inputting the next. Depending on the speed of your system and the applications you’re using, you may need more and longer delays between steps (seriously).

This script opens the Find & Replace window in PowerScribe and puts in a whole bunch of inputs in sequence to navigate through the menu and get rid of (respectively): two periods in a row, two spaces in a row, and a space before a period (‘ .’). You could easily add more variants like double commas, but these three automatically do the majority of polishing you could want.

We all know PowerScribe should do this automatically.

Searching Radiopaedia

Here’s another fun one. Let’s say you want to be able to search Radiopaedia faster (perhaps when you see a disease you don’t recognize in the EMR, something else typed into the history, a phrase from the comparison, or even just a few words you’ve specifically put into your own report to use this shortcut):

; Pressing Ctrl+Alt+R will copy the selected text to the clipboard and search Radiopaedia

^!r::
; Copy the selected text to the clipboard
SendInput, ^c
ClipWait

; Build the search URL for Radiopaedia
search_url := "https://radiopaedia.org/search?utf8=✓&q=" . UrlEncode(Clipboard)

; Open the search results in the default browser
Run %search_url%
return

; Custom UrlEncode function
UrlEncode(str)
{
static chars := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~"
encoded := ""

loop, parse, str
{
char := asc(A_LoopField)

; If the character is in the allowed set, keep it as is; otherwise, encode it
if (char >= 0x20 && char <= 0x7E && instr(chars, A_LoopField))
encoded .= A_LoopField
else
encoded .= "%" . format("{:02X}", char)
}

return encoded
}

Highlight some text and trigger this macro, and AHK will copy the text string to the clipboard and then search Radiopaedia for it.

Don’t worry about how we build the URL for searching Radiopaedia. It’s just an illustration, and it’s the kind of thing that’s partially unique to each site (the encoding function itself you can find online or make with ChapGPT).

SendInput is a more specific version of Send for inputting keystrokes and mouse clicks; either version will work for our purposes in all of these examples, and technically SendInput is actually faster and generally preferred for most processes. I’ve found it’s sometimes too fast and requires adding Sleep commands after to give programs time to respond. Feel free to try SendInput in your own scripts, and if things are too instantaneous, you can try switching commands or adding in the delays.

The one other command here you might want to know is ClipWait, which waits for text copied via Ctrl+C to be saved to memory before using it somewhere else. On some computers, I’ve found adding an additional short Sleep command after copying the text is still necessary as a buffer to make sure it works properly.

Macros for Common Exams

Obviously, you’ve been using voice commands in PowerScribe to use templates or other blocks of text, but if there are certain things you do dozens of times per day (e.g. insert normal chest radiograph), a button can be even more frictionless. (I don’t actually do this, but I know others who do).

AutoCorrection and Text Expansion

AHK lets you create your own version of AutoCorrect like on your phone via hotstrings. The syntax is easy.

; autocorrect examples
::teh::the
::dont::don't
::cant::can't
; text expansion is the same idea
::btw::by the way

Note this only works with typed text and not what PowerScribe transcribes. To make PowerScribe itself suck less, please see this post: Making the Most of PowerScribe.

More on hotstrings here. That page also includes this link to a fantastic AHK script that will automatically replace 4700 common English misspellings on the fly and has a built-in function to add more whenever you want by pressing Win+H. No reason to reinvent the wheel!

If you find yourself typing the same things frequently, text expansion can save you buckets of time (especially for emails).

Opening Up Frequently Used Websites

If there are certain websites you always open when you start your shift, you could create a macro to open them up all at once in the default browser:

^!t::
Run, https://gmail.com/
Run, https://mail.live.com/
Run, https://radiopaedia.org/
Run, https://www.imaios.com/en/e-anatomy
return

Creating Your Own Login Scripts

Another example would be making your own single sign-on when these processes have not already been bundled effectively by launching various programs using the Run command and then logging in to each as needed. It’ll take some trial and error to see how to navigate through the input fields and Sleeps to get the delays right.

Use Your Imagination

What repetitive tasks are sources of frustration in your day? How about getting through the multiple input fields needed to mark a case “correct” for peer review in PowerScribe? Even if the total time saved is minimal, sometimes just the reduced friction has a meaningful cognitive/psychological benefit.

I’ll give you two very idiosyncratic ones I use when reading for our hospital that may not apply to you but further illustrate the kind of approach you can use to spark your own joy.

AutoDelete BS Verbiage

Our hospital PowerScribe instance puts in a useless “dose reduction” paragraph in every CT report for MIPS/billing reasons. But, all of the templates we use include the text already in the technique field. So either you delete it every single time, or the duplicate stays at the bottom of the report distracting from your impression. Yuck.

^3::
WinActivate, PowerScribe
Send ^a
Send {delete}
Send {F4}
Return

In this quick macro, CTRL+3 (mapped to a button on my Shuttle) activates Powerscribe, highlights all of the current text, deletes it, and turns on dictation. I press it whenever I open up a new case.

Poor Man’s EMR Integration

At the hospital, we have EMR integration so that Epic loads the patient chart automatically when you open a case from the worklist. At home, no dice. Opening up charts manually is a huge waste of time and requires a whole bunch of clicks and keystrokes.

If in your practice you are forced to manually input MRNs or Accession numbers to look up patient information, you need to adopt a version of this.

^4::
SendInput, ^c
ClipWait
Sleep, 100
WinActivate, ahk_exe WFICA32.EXE
Send ^2
Sleep, 300
Send ^v
Sleep, 100
Send {Enter}
Sleep, 200
Send {Enter}
Return

In our worklist, I double-click on the accession number to highlight the number. I then activate this macro (through a button on my shuttle, which inputs CTRL+4), which copies the number, switches to Epic, opens the “Study Review” window, pastes the number, and hits {Enter} a couple of times to finish the process. Voila, a chart opened with a mouse click and a single button press. (Note: This exact script will almost certainly not work for you.)

Should I even have to do this? Of course not. Is this better than what I used to do? Absolutely.

Troubleshooting Scripts

Potentially tons to unpack on this dimension, but I want to discuss two common pitfalls I’ve found when working with AHK to control my computer:

Application Selection

When it comes to controlling your computer, one of the key commands is WinActivate. AHK comes with a helpful but scarily-named helper program called “Windowspy” that allows you to see information about active processes. When running Windowspy, you can see the name of the active program and other information that can be important for isolating features you need to make your scripts work.

If you want to use the WinActivate command to switch applications, you can find and copy the specific application title from there. Note that this isn’t always strictly necessary; it depends on how unique and consistent the title is. I have found that a simple “Powerscribe” is enough of a title. But, if I want to activate Epic running through an instance of Citrix, I need either the complete title (which for our instance changes with every login–annoying) or one of the alternative AHK WinActivate variants/variables identifiable through Windowspy (ahk_class, ahk_exe, ahk_pid).

Again, don’t be discouraged by needing some trial and error.

You can read more about WinActivate here.

Incomplete Macro Execution

If you find that a particular macro doesn’t seem to work consistently or cuts off in the middle, consider adding or lengthening the “sleeps” in between steps. You need to delay long enough for the last input to take effect. In my experience, the necessary delay for things like my punctuation macro varies with computer speed.

When our hospital “upgraded” to a new version of Epic, I had to more than double the delay steps in my DIY Epic Integration.

Conclusion

To reiterate, you’ll map these macros to your choice of underutilized keyboard keys or key combinations, and then as an optional step, you can map those triggers to the buttons on your magical device like a Shuttle or gaming mouse. I cannot stress how good these combinations work and feel.

Other than hands-free dictation, are most of these things a big deal? Not at all. They’re minor pain points. But they’re fixable, and removing these little bits of friction is awesome.

Just because the cost of inaction is invisible doesn’t mean it’s not real.

You can absolutely figure out what you want and how to do it.

Here’s a take-home script example you can use as a starting point that includes the dictation controls and the double space/period removal wrapped in the pause function.

(Okay, one more final point: many older scripts and examples online (including this one) use the mostly unchanged but slightly older syntax of AHK Version 1.1. There is now a 2.0 version. When you try to run a script using the older syntax, AutoHotkey will simply offer to download the older version. That’s 100% okay. They both work fine.)

; Set the initial state of the script to active
isActive := true

; Define the hotkey combination to toggle the script
^!p:: ; Ctrl + Alt + P
isActive := !isActive ; Toggle the state
if (isActive)
MsgBox Script is now active.
else
MsgBox Script is now paused.
return

; Place your always-on script code here

; Example hotkeys that are only active when the script is active
#If (isActive)

; toggle dictation
`::
WinActivate, PowerScribe
Send, {F4}
Return

; previous field
^`::
WinActivate, PowerScribe
Send, +{Tab}
Return

;next field
^1::
WinActivate, PowerScribe
Send, {Tab}
Return

; CTRL+q to remove extra spaces and periods
^q::
WinActivate, PowerScribe
Send ^h
Sleep, 100
Send ..
Send {tab}
Send .
Send {tab} {tab} {tab}
Send {Enter}{Escape}{Escape}{Escape}

Send ^h
Sleep, 100
Send {space}{space}
Send {tab}
Send {space}
Send {tab} {tab} {tab}
Send {Enter}{Escape}{Escape}

Send ^h
Sleep, 100
Send {space}.
Send {tab}
Send .
Send {tab} {tab} {tab}
Send {Enter}{Escape}{Escape}{Escape}
return

#If

Automate away, folks.

18 Comments

RJohn 08.28.23 Reply

Thank you! I have been waiting for this!!!

nym 09.12.23 Reply

thanks for this, got me pushed into going hands free

a note, these scripts don’t work in the 2.0 version I guess they changed the scripting language. I initially tried 2.0 and gave up, went back to the 1.1 and things working good so far

2.0 must be kinda new, other tutorial videos I’ve watched seem to reference the 1.X scripting conventions

Ben 09.12.23 Reply

Yes, I mentioned that at the end. Things work just fine in the old version, and–as you noted–most examples online still use the old syntax as well, so I personally haven’t felt the need to revise and update.

nym 09.12.23

no one ever reads to the very end ;)

check out the X-keys keypads as another left hand device for folks that don’t want the wheel / scroll function. Lots of buttons packed in and good software and on board memory if you’re not getting too complex with key functions

Ben 09.13.23

If anyone has done a compare/contrast between x-keys and the cheaper options like those from other companies like the ones I’ve linked to above, let me know.

SR 09.13.23 Reply

I just bought the contour last week. I downloaded the contour driver and the autohotkey app. And everything is working on my home computer.

Im going to try it at work tomorrow. I assume this will only work if my work computer allows me to download the contour driver and the autohotkey app?

Ben 09.14.23 Reply

Yes, you need to be able to install the contour shuttle driver. Usually most IT departments are willing to install software to manage hardware peripherals.

For AHK, see the above discussion. You can install the software and run the scripts as normal, or you can try to compile your script at home into an .exe file and then bring it over via a usb key, onedrive, a shared drive folder, or whatever is allowed. Depends on your local security situation, but you can often run that even in situations where you can’t install software because the permissions for installation and for execution are different. Most places only need an administrator login/password for installation.

Neil 01.18.24 Reply

Great article! Another script that I’ve found helpful is to have a button to automatically push the current study into PowerScribe – some places may have this automatically happen via their worklist manager, but our Epic-driven worklists force us to click the “Dictate” button in Epic. To avoid that, I use the script below so that clicking NumLock will launch the current study in PowerScribe to have it ready to dictate.

NumLock::Epic_Dictate()
Epic_Dictate()
{
WinActivate Hyperspace
Send !d
}

Neil Lall 02.19.24 Reply

Came back to this page to steal some more of your script snippets and realized you were using a more complex method of making the script active/inactive. You can also easily do this just by adding something like

^!p::Suspend

That simple line would use your ctrl-alt-p to “suspend” the whole script so you could use the keys normally. It also has a nice benefit of changing the AHK icon from an H to an S to give a subtle visual that your script is suspended.

Another nice benefit is for when you have a specific key (or combination) mapped to your script, but it’s also needed as a hotkey within a specific application. E.g. What if you wanted to make “F4” a global hotkey to toggle dictation in PS off/on. You’d use something like the below to suspend and then un-suspend the capture of “F4” so that the key does its original purpose instead of just re-triggering the script ad infinitum.

{F4}::
WinActivate PowerScribe
Suspend
Send {F4}
Suspend
Return

Ben 02.23.24 Reply

Suspend does look more elegant. I’m sure there are better ways to do a lot of what I’ve been using for those more savvy than I am!

Dustin Yontz 02.27.24 Reply

I’ve had the hardest time figuring out how to get AHK to work for Philips speechmike. Anyone out there have some success and willing to share their script?

Eric Kinder 03.06.24 Reply

Nice blog! I’ve been using AHK in radiology for years. I want to emphasize using ChatGPT 4 (not free 3.5) for script generation–it’s next level. Like with any coding, it can help you do things an order of a magnitude faster, with annotated scripts. Or, it can allow you to do things you never would have attempted because it would have been too complex or time consuming. Just make sure to tell the AI whether you use AHK 1.1 or 2.0.

Ben 03.12.24 Reply

Absolutely, the AI tools are really very helpful here.

Leave a Reply