19 |
20 | Stable version: [**`2.2025.7`**](https://github.com/firefinchdev/whatsapp-desktop-dark/releases/tag/2.2025.7) (Check if this version matches with your version of WhatsApp. The mod will still work even if the versions doesn't match)
21 |
22 | This mod is a simple tweak for [WhatsApp Desktop **Official** version](https://www.whatsapp.com/download/) to enable dark mode until it is released officially. It works for Windows and macOS.
23 |
24 | ⚠ **Please note that this script won't work with Microsoft store or App store version of WhatsApp desktop. You need to use the direct downloaded version linked above**
25 |
26 | ### DISCLAIMER:
27 | > **This is not an official version of WhatsApp Desktop Dark mode. So make sure to read through this document before you use this mod because i won't be responsible for any damage you've done yourself trying to install this mod (It's not even that hard 🙄). The coding/styling behind this tweak has full transparency since I've made it open-source right here.**
28 |
29 | ### Steps to Install:
30 | 1. [Download](https://github.com/firefinchdev/whatsapp-desktop-dark#downloads) the files.
31 | 2. [Run](https://github.com/firefinchdev/whatsapp-desktop-dark#installation) the downloaded files.
32 | 3. Open WhatsApp Desktop -> Settings -> Chats -> Theme and select Dark.
33 |
34 | # Is this safe?
35 | The answer is right there on the top. **This is an open source project**. which literally means that you can see through the source code of this mod. So **I CANNOT STEAL YOUR MESSEGES, I CANNOT TAP INTO YOUR CALLS**. If you're like one of the "Anti-Vaccination" parents, please avoid using this mod. Wouldn't you rather use the official WhatsApp version with a simple tweak like this than using some random third-party app just to have a proper dark UI?
36 |
37 | **NOTE:** This script will not replace any of the functionality related coding inside WhatsApp. Just the styling.
38 |
39 | # Downloads
40 |
41 | **NOTE:** *THIS IS NOT A MODDED VERSION OF WhatsApp*. WhatsApp Desktop Dark Mode is a simple set of styling that needs to be injected to the source files of your current installation of WhatsApp. I've made it much easier by creating a installation script using NodeJS and made it easier furthermore by creating simple executable files using [nexe](https://github.com/nexe/nexe). If you don't trust the executable files I've provided below, you can build and run it from the source code itself by following the instructions in the next section.
42 |
43 | - [All releases (with source)](https://github.com/firefinchdev/whatsapp-desktop-dark/releases)
44 | - Windows (x86 & x64) - [Download](https://github.com/firefinchdev/whatsapp-desktop-dark/releases/download/2.2025.7/WADark-2.2025.7-Windows.zip)
45 | - macOS (x64) - [Download](https://github.com/firefinchdev/whatsapp-desktop-dark/releases/download/2.2025.7/WADark-2.2025.7-macOS.zip)
46 |
47 | ## Installation
48 |
49 | Make sure you have [WhatsApp Desktop](https://www.whatsapp.com/download/) installed in your PC or mac first.
50 |
51 | ⚠ **Please note that this script won't work with Microsoft store or App store version of WhatsApp desktop. You need to use the direct downloaded version linked above**
52 |
53 | Make sure WhatsApp Desktop is running before you start the installation. Then wait for the process to finish. That's it.
54 |
55 | Simply extract the downloaded ZIP file and run `WADark.exe` on windows. For macOS you'll need to make `WADark` executable first, by executing `chmod +x WADark` from the root of extracted directory. Then run it with `WADark` from the same directory.
56 |
57 | **NOTE:** It's recommended not to delete the script folder after the installation because the script automatically creates a backup of your original WhatsApp source in case you need to revert back to the original version.
58 |
59 | ### How to remove the mod?
60 | Simply rerun the script and it will ask you to restore the backup made by any previous installations. (Or use the button on top left to switch to light mode)
61 |
62 | ### My WhatsApp turned back to light theme after sometime. What to do?
63 | This means that your WhatsApp build is probably auto updated to a newer version. Rerun the script and It'll tell you if a new version is available. Even if the dark mode version is not updated, just rerun the script and install the dark mode again.
64 |
65 | # Build the installation script yourself
66 |
67 | Building process is really simple even if you're not a pro developer. You will just need [NodeJS](https://nodejs.org) and [NPM](https://www.npmjs.com/) (Usually packed with NodeJS) installed on your computer to build and run the script yourself.
68 |
69 | Clone or download this repository, `cd` to that directory and enter the following commands to start the script. There are two methods you can run the script.
70 |
71 | **Method 1 (Build binaries)**
72 |
73 | 1. `npm install` (Wait for this to finish)
74 | 2. `npm run dist-win` (Windows) or `npm run dist-mac` (macOS)
75 | 3. Finally locate and run the binary file created in `dist/$PLATFORM/` directory.
76 |
77 | **Method 2 (Run directly from source)**
78 |
79 | 1. `npm install` (Wait for this to finish)
80 | 2. `npm run run-gui` for GUI installer
81 |
82 | Simple as that.
83 |
84 | # Support this project
85 |
86 | Involvement as a contributor by adding a few lines of code, fixing a bug, responding to issues, testing etc.. would be one of the most helpful methods you could support the project. If you're not a programmer but a generous person, you can send a small donation this way buy clicking the following button.
87 |
88 | [](https://www.paypal.me/mpwk?locale.x=en_US)
89 |
90 | Or you can buy me a "ko-fi" by clicking this button
91 |
92 | [](https://ko-fi.com/m4heshd)
93 |
94 | # Got an Issue?
95 |
96 | [Follow this link](https://github.com/firefinchdev/whatsapp-desktop-dark/issues) to submit your issues and please remember to be descriptive when submitting issues. Also don't forget to attach a small screenshot if the issue is style/GUI related.
97 |
98 | # Known issues
99 |
100 | **Windows:**
101 |
102 | - Automatically killing the WhatsApp process might take a little time or the process might repeat itself [`unfixable/SW-HW dependent`]
103 |
104 | # License
105 | "WhatsApp Desktop Dark Mode by firefinchdev" is licensed under [MIT License](LICENSE). So you are allowed to use freely and modify the application. I will not be responsible for any outcome. Proceed with any action at your own risk.
106 |
--------------------------------------------------------------------------------
/run-gui.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs-extra');
2 | const nodefs = require("fs");
3 | const ps = require('ps-node');
4 | const request = require('https').request;
5 | const {URL} = require('url');
6 | const semver = require('semver');
7 | const asar = require('asar');
8 | const path = require('path');
9 | const {spawn} = require("child_process");
10 | const open = require('open');
11 | const express = require('express');
12 | const xApp = express();
13 | let io = require('socket.io');
14 |
15 | let client = null;
16 | let version = '0';
17 | let started = false;
18 | let updURL = 'https://raw.githubusercontent.com/firefinchdev/whatsapp-desktop-dark/master/package.json';
19 | let bkPath = path.join(__dirname, 'backup', 'app.asar');
20 | let WAPath = null;
21 | let platform = process.platform;
22 | let execPath = '/Applications/WhatsApp.app/Contents/MacOS/WhatsApp';
23 | let command = 'WhatsApp.exe';
24 | let psargs = 'ux';
25 |
26 | if (platform === 'darwin') {
27 | WAPath = '/Applications/WhatsApp.app/Contents/Resources/app.asar';
28 | command = execPath;
29 | psargs = 'ax';
30 | }
31 |
32 | //Backend setup
33 | exports.startGUI = function () {
34 | xApp.use(express.static(path.join(__dirname, 'gui')));
35 |
36 | let xServ = xApp.listen(3210, function () {
37 | console.log('WADark GUI installer backend started');
38 | openInstaller();
39 | });
40 |
41 | io = io(xServ, {
42 | pingTimeout: 90000
43 | });
44 |
45 | io.on('connection', function (socket) {
46 | if (!started) {
47 | client = socket;
48 | console.log('WADark installer client connected. ID - ' + socket.id);
49 |
50 | //Incoming messages
51 | client.on('startInstall', function () {
52 | setOLTxt('Identifying process..');
53 | if (fs.existsSync(bkPath)) {
54 | ask('Current backup will be replaced and it cannot be undone. Are you sure want to continue?', function () {
55 | validateAndStart(false);
56 | }, function () {
57 | say('Hope you\'re enjoying WhatsApp dark.. :)');
58 | hideOL();
59 | });
60 | } else {
61 | validateAndStart(false);
62 | }
63 | });
64 |
65 | client.on('startRestore', function () {
66 | setOLTxt('Identifying process..');
67 | if (fs.existsSync(bkPath)) {
68 | validateAndStart(true);
69 | } else {
70 | say('Unable to locate the backup file');
71 | hideOL();
72 | }
73 | });
74 |
75 | client.on('endApp', function () {
76 | console.log('Quitting the installer..\n');
77 | client.disconnect();
78 | process.exit(0);
79 | });
80 |
81 | client.on('checkUpd', function () {
82 | checkAppUpd(false);
83 | });
84 |
85 | if (platform === 'darwin') {
86 | setMacUI();
87 | }
88 |
89 | startInit();
90 | } else {
91 | console.log('Client connection rejected. ID - ' + socket.id);
92 | socket.emit('setOLTxt', 'One instance of the process is already running.. Please restart if this is an error.');
93 | }
94 | });
95 | };
96 |
97 | //Backend functions
98 | function startInit() {
99 | showOL('Reading version info..');
100 | fs.readJson(path.join(__dirname, 'info.json'), (error, infoJSON) => {
101 | if (!error) {
102 | version = infoJSON.version;
103 | // version = "0.3.4940";
104 | setVersion('v' + version);
105 | checkAppUpd(true);
106 | } else {
107 | console.log(error);
108 | start();
109 | }
110 | });
111 | }
112 |
113 | function checkAppUpd(isStart) {
114 | showOL('Checking for updates..');
115 | let req = request(new URL(updURL), function (res) {
116 |
117 | res.on('data', (d) => {
118 | let latest = JSON.parse(d.toString())['version'];
119 | if (semver.lt(version, latest)) {
120 | ask('A new update is available (v' + latest + '). Would you like to download?', openDownload, start);
121 | } else {
122 | if (isStart) {
123 | start();
124 | } else {
125 | hideOL();
126 | say('Your script copy is up to date.');
127 | }
128 | }
129 | });
130 | });
131 |
132 | req.on('error', (e) => {
133 | console.log(e);
134 | if (isStart) {
135 | start();
136 | } else {
137 | hideOL();
138 | }
139 | });
140 |
141 | req.end();
142 | }
143 |
144 | function start() {
145 | endInit(fs.existsSync(bkPath));
146 | }
147 |
148 | function validateAndStart(isRestore) {
149 | if (platform === 'darwin') {
150 | if (fs.existsSync(WAPath)) {
151 | startInstall(isRestore);
152 | } else {
153 | say('Unable to locate your WhatsApp Desktop installation. Check again and retry.');
154 | hideOL();
155 | }
156 | } else {
157 | startInstall(isRestore);
158 | }
159 | }
160 |
161 | function startInstall(isRestore) {
162 | started = true;
163 | ps.lookup({
164 | command: command,
165 | psargs: psargs
166 | }, function (err, resultList) {
167 | if (err) {
168 | console.log(err);
169 | } else {
170 | if (resultList.length) {
171 | setOLTxt('Please close WhatsApp Desktop manually to continue installation.. (Please wait if you already have)');
172 | if (platform === 'win32') {
173 | WAPath = resultList[0].command;
174 | execPath = WAPath;
175 | }
176 | startInstall(isRestore);
177 | } else {
178 | if (WAPath) {
179 | if (isRestore) {
180 | restoreBackup(WAPath);
181 | } else {
182 | applyDarkStyles(WAPath);
183 | }
184 | } else {
185 | say('WhatsApp process not found. Make sure WhatsApp desktop is running before installing dark mode.');
186 | hideOL();
187 | started = false;
188 | }
189 | }
190 |
191 | }
192 | });
193 | }
194 |
195 | function applyDarkStyles(procPath) {
196 | try {
197 | let dir = path.dirname(procPath);
198 | let fullpath = path.join(dir, 'resources', 'app.asar');
199 |
200 | if (platform === 'darwin') {
201 | fullpath = procPath;
202 | }
203 |
204 | setOLTxt('Backing up..');
205 | fs.copySync(fullpath, bkPath);
206 |
207 | setOLTxt('Extracting..');
208 | let extPath = path.join(__dirname, 'extracted');
209 | asar.extractAll(fullpath, extPath);
210 |
211 | setOLTxt('Injecting styles..');
212 | let indexPath = path.join(extPath, "index.html");
213 | let rendererPath = path.join(extPath, "renderer.js");
214 | if (fs.existsSync(rendererPath)) {
215 | try {
216 | let updatedRenderer = nodefs
217 | .readFileSync(rendererPath)
218 | .toString()
219 | .replace("DARK_MODE: !prod", "DARK_MODE: true");
220 |
221 | nodefs.writeFileSync(rendererPath, updatedRenderer);
222 |
223 | let newAsar = path.join(__dirname, 'app.asar');
224 | asar.createPackage(extPath, newAsar, function () {
225 | setOLTxt('Replacing files..');
226 | try {
227 | fs.copySync(newAsar, fullpath);
228 |
229 | setOLTxt('Cleaning up..');
230 | fs.removeSync(extPath);
231 | fs.removeSync(newAsar);
232 |
233 | say('All done. Go to WhatsApp Desktop -> Settings -> Chats -> Themes and select Dark');
234 |
235 | let WAPP = spawn(execPath, [], {
236 | detached: true,
237 | stdio: ['ignore', 'ignore', 'ignore']
238 | });
239 | WAPP.unref();
240 | startInit();
241 | started = false;
242 | } catch (error) {
243 | console.log(error);
244 | setOLTxt('An error occurred. Cleaning up..');
245 | fs.removeSync(extPath);
246 | fs.removeSync(newAsar);
247 | startInit();
248 | started = false;
249 | }
250 | });
251 | } catch (error) {
252 | console.log(error);
253 | hideOL();
254 | started = false;
255 | }
256 | } else {
257 | say('\x1b[31m%s\x1b[0m', 'Failed to extract WhatsApp source.');
258 | hideOL();
259 | started = false;
260 | }
261 | } catch (error) {
262 | console.log(error);
263 | hideOL();
264 | started = false;
265 | }
266 | }
267 |
268 | function restoreBackup(procPath) {
269 | setOLTxt('Restoring original version of the application...');
270 | started = true;
271 | let dir = path.dirname(procPath);
272 | let fullpath = path.join(dir, 'resources', 'app.asar');
273 |
274 | if (platform === 'darwin') {
275 | fullpath = procPath;
276 | }
277 |
278 | try {
279 | fs.copySync(bkPath, fullpath);
280 |
281 | say('All done. Make sure to let the developers know if something was wrong.. :)');
282 | let WAPP = spawn(execPath, [], {
283 | detached: true,
284 | stdio: ['ignore', 'ignore', 'ignore']
285 | });
286 | WAPP.unref();
287 | hideOL();
288 | started = false;
289 | } catch (error) {
290 | say('An error occurred while restoring.');
291 | console.log(error);
292 | hideOL();
293 | started = false;
294 | }
295 | }
296 |
297 | function openDownload() {
298 | (async () => {
299 | await open('https://github.com/firefinchdev/whatsapp-desktop-dark/releases/latest');
300 | })();
301 | }
302 |
303 | function openInstaller() {
304 | (async () => {
305 | await open('http://127.0.0.1:3210/');
306 | })();
307 | }
308 |
309 | //Client functions
310 | function setOLTxt(text) {
311 | client.emit('setOLTxt', text);
312 | }
313 |
314 | function showOL(text) {
315 | client.emit('showOL', text);
316 | }
317 |
318 | function hideOL() {
319 | client.emit('hideOL');
320 | }
321 |
322 | function setMacUI() {
323 | client.emit('setMacUI');
324 | }
325 |
326 | function setVersion(ver) {
327 | client.emit('setVersion', ver);
328 | }
329 |
330 | function endInit(isBkAvail) {
331 | client.emit('endInit', isBkAvail);
332 | }
333 |
334 | function ask(text, yes, no) {
335 | client.emit('ask', text, function (resp) {
336 | if (resp) {
337 | yes();
338 | } else {
339 | no();
340 | }
341 | });
342 | }
343 |
344 | function say(msg) {
345 | client.emit('say', msg);
346 | }
--------------------------------------------------------------------------------