AutoHotkey v2 GUI → HTML: The Complete Control Mapping Cheat Sheet

 

AutoHotkey v2 GUI → HTML Mapping

Since WebAssembly requires HTML GUIs I’ve mapped out common AutoHotkey GUI elements and thier HTML counterparts.

Reference for translating AHK v2 GUI controls to their browser equivalents.

Quick Reference Table

AHK v2 ControlHTML EquivalentNotes
Text<label> or <span>Use <label for> when it names an input, <span> otherwise
Edit<input type=”text”>Single-line default
Edit +Multi<textarea>Different tag, not an attribute
Edit +Password<input type=”password”>Toggle by swapping the type attribute
Edit +Number<input type=”number”>Gets its own spinner, so no separate UpDown needed
Edit +ReadOnly<input readonly>readonly still submits; disabled does not
Button<button>Add type=”button” so it does not submit a form
Checkbox<input type=”checkbox”>Label text is a separate element in HTML
Radio<input type=”radio” name=”grp”>Grouping is by shared name attribute, not by add-order
DropDownList (DDL)<select> + <option>Closest one-to-one match in the whole list
ComboBox<input list=”x”> + <datalist>Editable dropdown; datalist is the suggestion source
ListBox<select size=”8″>size > 1 turns a select into a list box
ListBox +Multi<select multiple>Read via .selectedOptions
ListView<table> + <thead>/<tbody>Sorting, selection, and icons are all hand-written JS
TreeViewnested <ul>/<li> or <details><details> gives free expand/collapse with no JS
Progress<progress>Omit the value attribute for an indeterminate bar
Slider<input type=”range”>min / max / step attributes
UpDown<input type=”number”>Merged into the input; not a separate control in HTML
GroupBox<fieldset> + <legend>legend renders as the caption, same as AHK
Tab3buttons + <div> panelsNo native tab element; JS toggles panel visibility
Picture<img>Embed as base64 data URI to keep one self-contained file
Link<a href>AHK Link already uses <a> markup, so this is near-identical
Hotkey<input> + keydown captureNo native control; capture the event and format it yourself
DateTime<input type=”datetime-local”>Also date, time, month, week variants
MonthCal<input type=”date”>Browser supplies its own calendar popup
StatusBarfixed-position <div>position:fixed; bottom:0
Custom / ActiveXno equivalentRebuild in JS, or use <canvas> for drawn controls
MsgBoxalert() / confirm()Or <dialog> for a styled, non-blocking version
InputBoxprompt()Crude but functional; <dialog> for anything real
FileSelect<input type=”file”>Returns a File object, never a path. See caveats
DirSelectno real equivalentwebkitdirectory picks a folder’s files, not a path
ToolTiptitle=”” or CSS :hovertitle is free but ugly and slow to appear
Menu / MenuBar<nav> + <ul> + CSSFully hand-built; no native menu bar in HTML

Side-by-Side Code

AHK on the left, HTML on the right. Same control, same intent.

1. The window itself

AutoHotkey v2
MyGui := Gui("+Resize", "My Tool")
MyGui.BackColor := "202020"
MyGui.SetFont("s10 cWhite", "Segoe UI")
MyGui.Show("w400 h300")
HTML + CSS
<title>My Tool</title>
<body style="background:#202020;
  color:#fff; font:10pt 'Segoe UI';
  width:400px; height:300px;">
</body>

The browser tab is your title bar. +Resize is the default in a browser, not something you opt into.

2. Text label and Edit box

AutoHotkey v2
MyGui.Add("Text", "x10 y10 w80", "Name:")
NameBox := MyGui.Add("Edit"
    , "x100 y10 w200 vUserName")
; read it later
Value := NameBox.Value
HTML + JS
<label for="userName">Name:</label>
<input type="text" id="userName"
       style="width:200px;">
// read it later
let v = document
  .getElementById('userName').value;

AHK’s vVarName becomes HTML’s id. Both are the handle you grab the value by. Note that .Value and .value line up almost exactly.

3. Button with a click handler

AutoHotkey v2
Btn := MyGui.Add("Button"
    , "x10 y50 w100 h30", "Click Me")
Btn.OnEvent("Click", RunIt)
RunIt(Ctrl, Info) {
    MsgBox("Clicked!")
}
HTML + JS
<button type="button" id="btn"
        style="width:100px; height:30px;">
  Click Me
</button>
document.getElementById('btn')
  .addEventListener('click', () => {
    alert('Clicked!');
  });

.OnEvent(“Click”, Fn) and .addEventListener(‘click’, Fn) are the same idea with different spelling. The handler signature differs: AHK passes (Ctrl, Info), JS passes a single event object.

4. Checkbox and Radio

AutoHotkey v2
Chk := MyGui.Add("Checkbox"
    , "x10 y90 Checked", "Enable logging")
MyGui.Add("Radio", "x10 y120 Checked", "Fast")
MyGui.Add("Radio", "x10 y145", "Slow")
If (Chk.Value)
    LogIt()
HTML + JS
<label><input type="checkbox" id="log"
  checked> Enable logging</label>
<label><input type="radio" name="spd"
  checked> Fast</label>
<label><input type="radio" name="spd">
  Slow</label>
if (document.getElementById('log').checked)
    logIt();

Trap: AHK groups radios by consecutive add-order. HTML groups them by a shared name attribute. A converter must invent a group name per run of adjacent Radio controls. Also: AHK reads .Value (1 or 0), HTML reads .checked (true/false), not .value.

5. DropDownList

AutoHotkey v2
Dd := MyGui.Add("DDL"
    , "x10 y180 w150 Choose2"
    , ["Red", "Green", "Blue"])
Picked := Dd.Text     ; "Green"
Index  := Dd.Value    ; 2
HTML + JS
<select id="clr" style="width:150px;">
  <option>Red</option>
  <option selected>Green</option>
  <option>Blue</option>
</select>
let s = document.getElementById('clr');
let picked = s.value;          // "Green"
let index  = s.selectedIndex;  // 1

Trap: AHK indexes from 1, JS from 0. Choose2 becomes selected on the second option, and selectedIndex reads 1 for it. Every index in a converter needs an off-by-one adjustment.

6. ListView

AutoHotkey v2
LV := MyGui.Add("ListView"
    , "x10 y220 w380 h150"
    , ["Name", "Size", "Date"])
LV.Add("", "File1.txt", "2 KB", "8/27")
LV.Add("", "File2.txt", "5 KB", "8/26")
LV.ModifyCol(1, 150)
Row := LV.GetNext()
Txt := LV.GetText(Row, 1)
HTML + JS
<table id="lv">
 <thead><tr>
  <th style="width:150px">Name</th>
  <th>Size</th><th>Date</th>
 </tr></thead>
 <tbody>
  <tr><td>File1.txt</td>
      <td>2 KB</td><td>8/27</td></tr>
  <tr><td>File2.txt</td>
      <td>5 KB</td><td>8/26</td></tr>
 </tbody>
</table>

Biggest gap in the whole list. AHK’s ListView gives you sorting, row selection, checkboxes, icons, and column dragging for free. An HTML table gives you rows and columns and nothing else. Every one of those behaviors is JS you write yourself (or a grid library you pull in).

7. GroupBox and Tab3

AutoHotkey v2
MyGui.Add("GroupBox"
    , "x10 y10 w200 h80", "Options")
MyGui.Add("Checkbox", "xp+10 yp+25", "A")
Tabs := MyGui.Add("Tab3"
    , "x10 y100 w380 h200"
    , ["General", "Advanced"])
Tabs.UseTab(1)
MyGui.Add("Text",, "On tab 1")
Tabs.UseTab()   ; back to no tab
HTML + JS
<fieldset>
  <legend>Options</legend>
  <label><input type="checkbox"> A</label>
</fieldset>
<div>
 <button onclick="show(0)">General</button>
 <button onclick="show(1)">Advanced</button>
</div>
<div class="pane">On tab 1</div>
<div class="pane" hidden>On tab 2</div>

GroupBox maps cleanly. Tab3 does not: HTML has no tab control, so you build one from buttons plus panels plus a show() function. Note also that AHK’s UseTab is stateful (everything added afterward lands on that tab) while HTML nesting is explicit.

8. Slider and Progress

AutoHotkey v2
Sld := MyGui.Add("Slider"
    , "x10 y10 w200 Range0-100", 50)
Sld.OnEvent("Change", OnSlide)
Prg := MyGui.Add("Progress"
    , "x10 y50 w200 h20", 25)
Prg.Value := 75
HTML + JS
<input type="range" id="sld"
  min="0" max="100" value="50"
  style="width:200px;">
sld.addEventListener('input', onSlide);
<progress id="prg" max="100" value="25"
  style="width:200px;"></progress>
prg.value = 75;

Two of the cleanest mappings. Range0-100 splits into min and max. Use the input event, not change, to fire while dragging like AHK does.

9. Reading every value at once (Gui.Submit)

AutoHotkey v2
Data := MyGui.Submit(false)
; Data.UserName
; Data.Enabled
; Data.Color
HTML + JS
<form id="f"> ...controls... </form>
let data = Object.fromEntries(
  new FormData(document.getElementById('f'))
);
// data.userName, data.color ...

FormData is the closest analog to Submit. It needs each control to carry a name attribute, so a converter should emit both id and name from AHK’s vVarName. Caveat: unchecked checkboxes are omitted from FormData entirely rather than reporting 0.

Positioning: the real work

AHK positions by absolute pixel: x10 y20 w100 h30. HTML flows elements one after another and expects CSS to handle layout. Three ways to bridge that:

ApproachHowTrade-off
Absoluteposition:absolute; left:10px; top:20px; width:100pxPerfect 1:1 with AHK. Easiest to auto-convert. Does not reflow, so it breaks on phones
FlexboxGroup controls into rows/columns, let CSS space themResponsive and idiomatic. Requires inferring intent from coordinates
CSS GridMap the x/y grid onto grid-column / grid-rowGood for form-like layouts. Most work to derive automatically

For a converter: start with absolute positioning. It is a direct arithmetic translation, it renders identically to the AHK original, and it lets you verify the conversion is faithful before worrying about responsiveness.

Relative-position options need resolving before you can emit CSS:

AHK optionMeaning → what the converter must do
xp / ypSame as previous control. Track the previous control’s box and substitute the number
xp+10 / yp+25Previous plus offset. Same tracking, plus arithmetic
x+10 / y+10Right of / below previous control’s edge. Needs the previous width/height
xm / ymBack to the left/top margin. Resolve to the margin value
xs / ysBack to the last Section anchor. Track section state
(omitted w/h)The hard one. AHK auto-sizes from text and font metrics. A static converter cannot know the result without measuring, so let CSS auto-size instead and accept some drift

Event Mapping

AHK OnEventJS eventNotes
“Click”clickDirect match
“DoubleClick”dblclickDirect match
“Change”input (or change)Use input to fire per keystroke; change only fires on blur
“Focus” / “LoseFocus”focus / blurDirect match
“ContextMenu”contextmenuCall preventDefault() to suppress the browser menu
“ItemSelect” (LV)click on <tr>Hand-rolled; you track the selected row yourself
“ColClick” (LV)click on <th>Sorting logic is entirely yours
Gui “Close”beforeunloadCannot be cancelled silently; browser controls the prompt
Gui “Size”resize / ResizeObserverOften unnecessary once CSS handles layout
Gui “DropFiles”drop + dragoverGives File objects, never paths
Hotkey / hotstringkeydown, page-scoped onlyWorks only while the page has focus. No system-wide hotkeys, ever

Styling and Dark Theme

This is where HTML wins outright. Everything you fight for in an AHK dark theme is one CSS line in a browser.

AHK v2CSSComment
Gui.BackColor := “202020”background:#202020Same
SetFont(“s10 cWhite”, “Segoe UI”)font:10pt ‘Segoe UI’; color:#fffSame
WM_CTLCOLOREDIT subclassinginput { background:#2d2d2d }Dozens of lines of DllCall become one rule
NM_CUSTOMDRAW for LV rowstr:nth-child(even) { … }No crash risk, no focus-loss bug
Dark LV header hackth { background:#2d2d30 }Trivial in CSS
Themed button text color (impossible)button { color:#fff }Just works
DPI scaling mathhandled by the browserNothing to do
Startup flash workaroundn/aDoes not happen

Add color-scheme: dark; to your CSS and the browser darkens scrollbars, native dropdown popups, and date pickers for you.

No HTML Equivalent

These are not conversion difficulties. They are browser sandbox limits, and no amount of clever code gets around them.

  • System-wide hotkeys — JS key events fire only when the page has focus
  • Send / Click / MouseMove — a page cannot drive other applications
  • WinActivate / WinMove / window management — no access to other windows
  • File paths — <input type=”file”> hands you contents, never a path. FileRead of an arbitrary path is impossible
  • Registry, INI files, DllCall, COM — no OS access at all
  • Tray icon and tray menu — no system tray from a page
  • Always-on-top, borderless, click-through, transparency — the browser owns the window chrome
  • Multi-monitor positioning — the page cannot place itself on a specific screen
  • Clipboard (unrestricted) — partial. Writing needs a user gesture, reading needs explicit permission, and ClipboardAll has no analog

Rule of thumb: the GUI layer converts well. The automation layer does not convert at all. A script whose GUI is a front-end for data in and results out ports cleanly. A script that drives other applications cannot become a web page at any level of effort.

If You Build the Converter

Parsing order that avoids most of the pain:

  1. Find the Gui() constructor. Pull the title, options, BackColor, and SetFont into page-level CSS.
  2. Walk every .Add() call in source order, keeping a running cursor of the previous control’s x, y, w, h so xp / yp / x+ / y+ / xm / xs can be resolved to absolute numbers.
  3. Emit one HTML element per control with position:absolute and the resolved coordinates.
  4. Map vVarName to both id and name. Auto-generate names for controls that lack a v-option.
  5. Collect .OnEvent() calls and emit matching addEventListener stubs with empty bodies.
  6. Leave every handler body as a TODO comment. Do not attempt to translate AHK logic in v1 of the tool.

Known parser traps worth handling early:

  • Consecutive Radio controls need a shared generated group name
  • Index off-by-one everywhere (AHK is 1-based, JS is 0-based)
  • Controls added inside a GroupBox or Tab are parent-relative in AHK, so nesting changes the coordinate origin
  • UseTab is stateful and affects every subsequent Add until reset
  • Omitted w/h means auto-size from font metrics, which is unresolvable statically
  • Controls added in a loop or with computed positions cannot be resolved without running the script
  • Continuation sections and line-continued Add calls must be joined before parsing

 

The time i got fired on the spot🔥 News 2026-08-27

AutoHotkey Newsletter

Howdy,

I was speaking to my son about looking for a job and working in shipping.

I told him of a time when I was about his age and had a part-time job helping ship t-shirts out of the owner’s garage.

I had done this for a few years for another company so I was in very familiar territory.

In addition to me, there were other people that helped out for this small, family run, business.

I helped make some improvements to the workflow like we did at our other company (moving frequently ordered items close to the packing station, cutting off tops of boxes so we can easily reach inside, etc.)

I was informed that customers were frequently receiving the wrong items so the owner came up with a new policy.   One person “picks” the order, the other verifies what is inside, seals it up, and initials its contents.

Not an unrealistic request and could help catch issues.

But it drastically slowed down our productivity.

You could rightfully argue “Joe, you were getting paid by the hour, what do you care about productivity?”

And I’ll have to say, at the time, I really didn’t think I could explain it to you.

I just knew that I was not the one pulling the wrong items.

One day we “got behind” so I decided to just pull them all, seal them up and ship them out.

Unfortunately for me the boss happened to come down that day and check in on us.  She noticed my colleague hadn’t signed off on the packages and asked why not.

I just told her that  I “knew” I was not the one pulling the wrong things.

She apologized to me but said the infamous Trump line: ” You’re fired❗”

I remember saying “that’s fine, I completely understand.”

I understood I had 💯% disobeyed her direct request and agreed she had full cause to fire me.

What I found interesting in retrospect that, even when I was ~19 years old, I had this “hard coded” passion for being productive.

How about you❓  Have you always wanted to save time?

Or is working smarter, not harder a new passion for you?

Now on with the show…

Learn how to Master VSCode Make the switch to v2 Intro to AutoHotkey Intermediate AutoHotkey Intermediate ObjectsGUIs are Easy w/AutoHotkey Access all our Courses; Quit when you want

stillmakingexcuses


Stop Micromanaging AI

A lot of people critique AI when they use it to do what they are experts in.

In this discussion we help solidify the point.

Why We Micromanage AI and How to Stop | Hero Extract

Why We Micromanage AI and How to Stop | Hero Extract

Then, in this video, we discuss doing a better job giving AI context and clear instructions.

Train AI With Your Hidden Preferences 🧠 | Hero Extract

Train AI With Your Hidden Preferences 🧠 | Hero Extract


Understanding Token Costs and Effort

If you’re baffled at understanding how you burn through tokens so fast, I 💯% don’t blame you❗

In this video we discuss approaches to keeping the context window as short as possible.

Medium Reasoning Mode Is All You Need 🎯 Stop Overpaying for AI | Hero Extract

Medium Reasoning Mode Is All You Need 🎯 Stop Overpaying for AI | Hero Extract


Easily Jump To parts of your Registry

During a Hero call we came up with the idea of how to easily jump to parts of your registry.

You can see how it was figured out in this video.

After the call I had AI build RegJump.

Recreating Regjump with Custom Code | Hero Extract

Recreating Regjump with Custom Code | Hero Extract


Improving People’s Lives: Story #101

We were working with a remote client and he was not loving having to copy/paste things back and forth between us.

We have an internal tool that uses DropBox which allows us to copy/paste remotely but installing DropBox is a bit much to ask for clients.   So I asked AI if we could use TailScale to be the “transporter”.

An hour later and we had a new tool that took no installation on client’s computers but I can copy and he can paste (and vice versa).  Much more efficient!  (btw I’m creating an App so I can also share the clipboard with my phone)


📚 What we’re reading


🤖 AI News that you Need to Know


⚡️Productivity tips:

One of my new clients does not spend a lot of time on the computer so I “Rocked her world” by telling her about the clipboard history tool in Windows 11 by hitting Windows  + V

How To Use Clipboard History in Windows 11 – Including Permanent Clipboard Items

How To Use Clipboard History in Windows 11 - Including Permanent Clipboard Items


🤣 A spot of Humor: So brutal❗

Ricky Gervais Brutally Tearing Down Celebrities To Their Face
Ricky Gervais Brutally Tearing Down Celebrities To Their Face


What we 🅰utomated this week with AutoHotkey #145

Most of this week went into Rust. 

An audio ripper with multi-threading and noise removal, a concept-search RAG system for chatting with local docs, and a media deduper that uses strata sampling to spot duplicate videos and audio fast enough to claw back hundreds of gigs.

I also fixed a license script that was tripping up 400+ downloads, pushed local transcription further with ASR and diarization, and switched the AI OCR tool to a single OpenRouter token so it can hit Claude and hundreds of other models.

In this week’s video I walk you through all of it, including the benchmarks where Rust image search went head to head with AutoHotkey and C++ (FindText) when I was rebuilding AutomateMyTask.

What we Automated this week #145
What we Automated this week #145


Clients say…

You guys changed my life! Thanks man — Marrion Mentoor


AHK Hero 🦸

Consider joining the 🦸Hero club! Members receive 25% off courses, consultations, tutoring, done for you project work, etc.    Currently we have over 603 hours of recordings teaching AutoHotkey that is exclusive to Hero members

During this Friday’s call we’re planning to discuss:

  • Help Hero Members with their Questions & Code
  • AHK Tools
    • Great discovery to get around install issues
    • ClipshareTailScale / ClipShare from Coworkers
    • OpenRouter Provisioning
    • Claude Settings
    • Creating v1 and v2 compatible scripts
    • Borrowing Firefox Cookies
    • Focus Guard
    • Examples using AutoHotkey.dll
    • WatchFolders example
  • Rust Tools
    • Unexpected benefit of using Rust
    • Virtual Camera
    • Updating Rust tools to provide Get Active Path with the path of the program
    • Rust DLL
    • FocusGuard (Prevent focus being stolen)
    • PhoneDeck
  •  Progressive Web Apps
    • ClipShare
    • Song Sync
    • Burgers and more
    • HenHouse

They also have access to exclusive & pre-released content and 2 hours a week where we help people with their automation issues


🗣️ 💭 Quotable quotes

  • Listen to something inspiring when you get up. It will change your day! — Joe Glines
  • The towels were so thick there I could hardly close my suitcase — Yogi Berra
  • Just do it. The meek may inherit the earth, but only after the bold are through with it — John Carlton

AHK Newsletter⏩SHARE WITH A FRIEND⏩

Do you know someone that would benefit from learning useful automation tips like the gems in today’s issue?  Take a second and forward this newsletter and bask in the glow of their undying gratitude.  Was this email forwarded to you?

Sign up for this Newsletter hereWe never share your email with anyone.  Ever!


the-Automator  Dedicated to Multiplying your Income

Joe, Isaias, Irfan and staff


P.S. Incidentally, regarding the intro, a couple of days after getting fired, I did hear from my work colleague.

They opened all of the orders I’d packed and confirmed they were all 💯% correct.  👀

What If Your Users Never Had Install to Your Software?

I Just Found Out My Rust Code Can Run In A Browser

(And It Changes My Whole Plan)

I was talking through a problem I’ve had for years, and an offhand comment completely reframed it for me. I want to share it, because if you write code that other people need to run, you have this same problem whether you’ve named it or not.


The problem I kept running into


I write in AutoHotkey, Rust, Python, and C++. I love all four. They’re fast, they’re capable, and they let me build genuinely useful things.

They also share one giant problem: somebody has to install an exe.

Have you ever built something great, handed it to someone, and watched it die right there? I have. Over and over. Here’s what actually happens:

  • Their work computer is locked down and IT says no
  • Antivirus flags it because it’s an unsigned exe from a guy on the internet
  • They get the “unknown publisher” warning and nope right out
  • They’re on a Mac, and my AutoHotkey script may as well be written in Latin
  • They just plain don’t want to run a random exe, which honestly is fair

Notice something about that list? Almost none of it is about my code. It’s all distribution. The tool works fine. It just never gets to run.

The people who most need a tool to save them time are very often the exact people who are not allowed to install one. That’s a rough irony to sit with.

So I looked at Progressive Web Apps


A Progressive Web App (PWA) is just a web page built so it can be installed like an app. You open a URL. If you want, you add it to your home screen or dock and it gets its own icon. It works offline. There’s no installer, no admin rights, no antivirus warning.

Great. Problem solved, right? Except I had a real hesitation, and maybe you’re already thinking it too: that means writing everything in JavaScript and giving up the languages I’m actually good at.

That’s where I was wrong.

WebAssembly, or WASM


I’d genuinely never heard of this until recently, so if it’s new to you, you’re in good company.

Browsers run JavaScript. About ten years ago they quietly added a second thing they can run: a compact binary format called WebAssembly, or WASM for short. Rust, C, C++, Go, and a bunch of other languages can compile to it.

Here’s the analogy that made it click for me. Think of a shipping container. It doesn’t matter whether you’re shipping bananas or bolts. You load it into the standard container, and every port in the world knows how to unload it, because the container is the standard, not the contents.

WASM is that container. I compile my Rust into it, and every modern browser knows how to run it. It doesn’t care that it started life as Rust.

So the thing I thought was a tradeoff isn’t one. A PWA isn’t me abandoning my stack. It’s a new place to ship my stack to.

Is it actually fast, though?


This was my next question, and I want to give you the honest answer rather than the hype version.

WASM is typically somewhere between 1.1 and 2 times slower than the same code compiled natively. Not the same. Slower. Anyone who tells you it’s identical to native is overselling it.

But that’s the wrong comparison, because native Rust can’t run in a browser tab at all. The real comparison is against JavaScript, which is the alternative for that job. And there, WASM is usually several times faster for heavy work.

ComparisonRoughly
WASM vs native Rust1.1x to 2x slower
WASM vs JavaScript (heavy work)2x to 10x faster

So I keep most of my speed and I gain the ability to reach people I could never reach before. I’ll take that trade every day.

The cool thing is most web developers only write JavaScript. When something needs real computation, they either send it to a server or it’s just slow. If you already write Rust or C++, you can do that work right on the user’s machine, fast. That’s a genuine edge, and it’s a weird one to suddenly discover you have.

Real tools that already run this way


This isn’t theoretical. A lot of serious software has already been compiled to WASM, and you can just use it:

  • SQLite, so you can open a real database file in a tab
  • ffmpeg, for converting and trimming video
  • DuckDB, for running SQL over millions of rows
  • Tesseract, for OCR
  • Python itself, through a project called Pyodide
  • Whisper, for speech to text, running entirely on your own machine
  • Doom. Obviously Doom. It’s always Doom.

What we can build, and what we honestly can’t


This is the part I want to be straight about, because it’s where I see people get excited and then hit a wall.

WASM makes code fast. It does not make the browser tab powerful. Everything still runs in the browser’s sandbox. There’s one rule that decides everything:

The user has to hand the tool its input. They pick a file, pick a folder, drag something in, or paste something. The tool cannot go looking on its own.
Works greatNot possible, at all
Hash a folder and find duplicate filesGlobal hotkeys
Chew through a giant CSV without Excel chokingRunning in the background or in the tray
Audio and image processingReading files the user didn’t pick
Reading formats the browser doesn’t knowControlling other apps or windows
Search and indexing over your own documentsAnything needing admin rights

Look at that right-hand column and you’ll notice something: that’s basically the list of things AutoHotkey is best at. Hotkeys, running in the tray, poking at other windows. So this isn’t AutoHotkey in a browser, and it never will be. Anyone promising you that is selling something.

It’s a different tool for a different job. AutoHotkey still owns my desktop. This is for reaching everybody else.

One catch worth knowing about


WASM itself runs everywhere. Windows, Mac, Linux, iPhone, Android, every modern browser. No exceptions worth worrying about.

But the ability to pick a whole folder and write files back to it is Chrome and Edge only. Not Safari, not Firefox, not on an iPhone. It does work on a Mac or on Linux, as long as they’re in Chrome or Edge.

Don’t get me wrong, that’s a real limitation. But compare it to what I have today: “you must be able to install an exe on Windows.” Going from that to “you need Chrome or Edge” is a massive upgrade in who I can reach. And locked-down corporate machines almost always have one of those two.

The part that surprised me most


I went into this thinking “no install” was a convenience thing. Nice to have. Saves a few clicks.

It turns out it’s bigger than that. Think about an analyst at a bank or a hospital who needs to open a 2GB file. They can expense a fifty dollar tool on a company card without blinking. What they cannot do is get an exe through an IT security review. That takes months, if it happens at all.

So a tool that runs in their browser isn’t competing on features. It’s the only option that’s actually available to them.

Same story for school Chromebooks, library computers, contractors on a client’s laptop, and anyone on a Mac who I’ve never been able to help at all. That’s a lot of people I’ve been writing off without really thinking about it.

What I’m doing next


I’ve got the toolchain installed and I’m starting small on purpose. First up is a boring little test: point it at a folder, hash every file in Rust compiled to WASM, find the duplicates, and time it against a JavaScript version doing the same thing.

Not glamorous. But it answers the questions that matter before I sink real time in: does the toolchain work, is the speed genuinely there on normal hardware, and does the folder permission prompt feel okay to a regular person or does it scare them off?

If that goes well, a handful of small single-purpose tools follow. One job each, done well. I’d rather ship five tiny things that work than one big suite that half works.

One thing I’ve learned over the years: the fastest way to kill a good idea is to plan it for six months instead of testing it in an afternoon. Life is an experiment. Try it, measure it, and let the results tell you whether to keep going.

How about you?


Have you ever built something useful and then watched it go nowhere because the other person couldn’t install it? Or been on the other side of it, stuck on a locked-down machine wanting a tool you weren’t allowed to have?

I’d genuinely like to hear about it. What tool would you want if the install problem just went away? Tell me and it might end up on my build list.

Now on with the show.


P.S. If you take one thing from this: the languages you already know are probably worth more than you think. I assumed reaching browser users meant starting over. It didn’t. It meant learning one new way to package what I’d already built. That’s a much smaller hill to climb, and it’s the kind of thing worth checking before you talk yourself out of something.