Disclaimer: Hacking apps is often against the Terms of Service. This article is purely theoretical and not an endorsement of the practice. Always Hack Responsibly.
Have you ever been extremely annoyed by a certain aspect of an app or website?
If you're anything like me, the answer is probably yes. If you're a lot like me, the answer is "yes, often."
Unless the app is an Electron app.
I don't remember how I learned about this possibility—sometimes, there's no stronger driving force than the need to fix bad design.
Let's be clear, though: this post—just like any worthy software endeavor—is about the journey rather than the solution. So follow along my adventure, see how I figured out the needed steps, and learn something about the command line, electron apps and hacking!
Note: while I use macOS High Sierra, this process can be similarly replicated on other operating systems.
Initially, I wanted to learn as much as I could about the Discord application. MacOS has a UI mechanism to explore application contents, which is a good "step zero." Manual exploration only goes so far, though, and soon I turned to the command line.
To answer my own question, I ran the following command:
ps x | grep Discord
ps lists all the processes running,
x includes the ones not attached to a shell (e.g. those started by clicking on application icons), and piping this output (
|) to the
grep command displays only the ones featuring the string
Discord. You can learn more at explainshell.com.
Here's the output, edited for readability:
1927 ?? S 0:00.08 /Applications/Discord.app/Contents/Frameworks/Electron Framework.framework/Resources/crashpad_handler --no-rate-limit --no-upload-gzip --database=/var/folders/sm/4v5p46v175d3x94qp56r37340000gn/T/Discord Crashes --metrics-dir=/var/folders/sm/4v5p46v175d3x94qp56r37340000gn/T/Discord Crashes --url=http://crash.discordapp.com:1127/post --handshake-fd=73
1928 ?? R 34:58.78 /Applications/Discord.app/Contents/Frameworks/Discord Helper.app/Contents/MacOS/Discord Helper --type=renderer --no-sandbox --autoplay-policy=no-user-gesture-required --force-color-profile=srgb --enable-features=SharedArrayBuffer --disable-features=MacV2Sandbox --service-pipe-token=5494336596696404231 --lang=en-US --app-path=/Applications/Discord.app/Contents/Resources/app.asar --node-integration=false --webview-tag=false --no-sandbox --preload=/Users/essential_randomness/Library/Application Support/discord/0.0.254/modules/discord_desktop_core/core.asar/app/mainScreenPreload.js --background-color=#2f3136 --num-raster-threads=2 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --service-request-channel-token=5494336596696404231 --renderer-client-id=6
The first process (#1927) seemed to be related to reporting app crashes. I assumed this because of
.../crashpad_handler in the app path, and the
url flag pointing to
http://crash.discordapp.com:1127/post (which is probably the server endpoint crash reports are communicated to).
The second process (#1928) was more promising. In particular I found the value of the
app-path variable (
/Applications/Discord.app/Contents/Resources/app.asar) worth exploring.
.asar extension of the file intrigued me. After a quick google search for "asar files", I found a GitHub repo explaining the format:
Asar is a simple extensive archive format, it works like tar that concatenates all files together without compression, while having random access support.
Luckily, the repository contained information on how to install the asar command- line utility (
npm install asar) and how to extract archived files from asar files. My next step was both obvious and easy.
Before making any changes, however, I decided to backup the original archive:
# Backup original file in case of emergency cd /Applications/Discord.app/Contents/Resources/ cp app.asar app_safe_copy.asar # Extract app.asar to a folder named "unpacked" asar extract app.asar unpacked/
After cracking this metaphorical treasure chest open, it was time to list (
ls) its content!
cd unpacked ls # output: > app_bootstrap common node_modules package.json ls app_bootstrap/ # output: > Constants.js bootstrap.js ... appSettings.js ...
For the first time I thought that (maybe) I could really pull this off!
I explored the JS files by opening them in VsCode to get some insight on the app's structure. This was interesting, but very slow.
To be faster, I decided to bet on a simple assumption: any file controlling the window width would have had to contain the string "width" itself!
Also, I could exclude the node_modules folder from my search because npm packages reserve this directory for external libraries.
# Find all the files containing the string width in the current folder, # but exclude the ones in the node_modules one. grep -iRl "width" ./ | grep -v node_modules # Output: > .//app_bootstrap/splash/index.js > .//app_bootstrap/splash/variables.json > .//app_bootstrap/splashScreen.js
This output was disappointing: the files were clearly related to the splash screen, which wasn't what I was looking to change. I tried going up in the top-level Discord folder (
/Applications/Discord.app/) and ran the command again, but the output wasn't much different.
It seemed my luck had ran out.
Rather than despair, I decided to go back to process #1928. The
preload flag held another interesting path, in a completely different location than the previous one:
It was time for another adventure!
# Once again, I searched for files containing the string "width". cd /Users/essential_randomness/Library/Application\ Support/discord/ grep -iRl "width" ./ | grep -v node_modules # Output > .//Preferences > ... > .//settings.json > ... > .//0.0.254/modules/discord_desktop_core/core.asar
This search yielded quite a number of files, so I decided to try to narrow it down further. Since I was looking to change the minimum width, I figured any related variable name would be called either minWidth or min_width. After all, we all take code readability seriously, don't we?
grep -iRl "min_width" ./ | grep -v node_modules # Output > .//0.0.254/modules/discord_desktop_core/core.asar grep -iRl "minWidth" ./ | grep -v node_modules # Output > .//0.0.254/modules/discord_desktop_core/core.asar > .//0.0.254/modules/discord_voice/discord_voice.node
core.asar looked really promising! Once again I extracted it and searched for the right file:
cd 0.0.254/modules/discord_desktop_core cp core.asar core_safe_copy.asar asar extract core.asar core_unpacked cd core_unpacked # Trying min_width first, as the value is likely a constant. # Constants use, by many code conventions, a capitalized style (i.e. "MIN_WIDTH"). grep -iRl "min_width" ./ | grep -v node_modules # Output: > .//app/mainScreen.js
.//app/mainScreen.js finally be The One? I immediately opened it, searched for "min_width" and...
const MIN_WIDTH = settings.get('MIN_WIDTH', 940); const MIN_HEIGHT = settings.get('MIN_HEIGHT', 500);
I knew I had to be onto something. With no clue about whether it would work (but tons of faith) I edited the code:
const MIN_WIDTH = settings.get('MIN_WIDTH', 0); const MIN_HEIGHT = settings.get('MIN_HEIGHT', 0);
Now all I needed to do was repack the changed asar file. Again, I made sure to create a backup of core.asar (
cp core.asar core_safe_copy.asar) before going further. Bricking Discord completely was a real possibility here!
With huge trepidation, I ran the final step:
# Remove the original app file and swap it with our edited code, repacked. rm core.asar asar pack core_unpacked core.asar
At this point I restarted Discord, hoping the changes would take effect. I placed my cursor at the app border, started dragging and... IT WORKED!
At this point, I had still one last curiosity. What would have happened if I had messed up while modifying the code?
This hammered down the importance of back ups. Since I had wisely created a copy of core.asar, I simply put the original code back in its place and the error was gone.
As a final warning, modifying code this way is likely against most Terms of Service (cue the usual Great Power => Great Responsibility speech).
Another aspect of code hacking to think about is side effects: Discord is not optimized to be displayed at lower sizes and the UI can be wonky. Since Electron apps use Chromium as a fronted, I modified the UI myself through the Developer Tools console (which Discord kindly makes available under "view > developer > developer tools").
Please remember that a very valid reason why developers don't want their code modified is that it can cause unexpected bugs in the app itself. If you choose to run a custom version of any code, don't go filing bugs unless you can reproduce them in the original app!
After going through all this, I found out there's an easier way to change Discord's window size that does not require modifying the source code.
But, you know, where would the fun be in that?