Before last Monday's development meeting, I was wondering if I looked okay because I'd been running my hands through my hair in frustration for quite a bit. (It wasn't the best of mondays.) In the past, when I was still on Mac, I used the Photobooth app for these kinds of things but I hadn't installed any camera app on my Linux machine.
I considered looking for a camera app, I know I can open Zoom/Chrome/... and look for my webcam settings. I considered looking for a website that showed you your webcam, I imagined buying a mirror for my home office. But in the end I'm a developer and decided to just build something myself.
That's how I came up with the idea of creating a "mirror app". I didn't want it to be all that fancy as it was to service one single, simple purpose: be a mirror. Since most of the time I’m either in my browser or on the command line, the only other requirement I had was that it could be started from both those places.
- https://mirror.ambassify.com which works from your browser. Go ahead, give it a try.
- A little something for the developers among us. Anyone who has Node.js installed can run this from their command line to launch the Ambassify mirror:
The project was surprisingly easy to build. I got it done in about an hour or two and approximately 100 lines of code. You can find the code in our GitHub repository.
The tools I used:
- GitHub pages
Getting started with Electron was pretty easy by following their Quick Start guide. The result:
Some things to take note of:
- I added Electron as a regular dependency instead of a devDependency. This is how we make it easy for everyone to run the app from their command line (see later.)
- Set the
autoHideMenuBaroption to true to get a nice clean window without clutter.
- Added a listener for
new-windowevents that launches the URL in our users’ regular browser. (I added some Ambassify links for people that want to learn more about us.)
- Added a listener for
window-all-closedevents to fully quit the app as we don’t expect people to want to keep the mirror in their task bar after taking a quick look.
Setting up the webcam
Setting up the webcam is done in our
renderer.js script. This script is referenced by the
index.html file we created earlier in Electron’s Quick Start and runs in the browser context, thus giving us access to browser API’s. The code snippet below is all that’s needed to get everything up and running and can be found on MDN (my favorite source of info for all things web.) It does assume you added a
<video /> tag in the
And we’re done. Running
npm start at this point will launch the mirror app.
Making it npx-able
One of our requirements was to make this easy to run from the command line. The solution I had in mind was to make it executable by running
npx mirror-me. This turned out to be pretty easy to accomplish too. Add a
bin section to the
package.json file, make sure the referenced binary executes the same
npm start command mentioned above and publish the package to NPM.
The biggest “gotcha” here is that you need to add Electron as a regular dependency because a devDependency is not installed when your package is installed as a dependency or globally by npx.
Instead, I could have built the Electron app into a binary and included that when publishing to npm but that means creating binaries for multiple operating systems, potentially having to deal with code signing and permissions, etc. By running the Electron app from source we just don’t have to deal with any of that.
A web version
After finishing up the Quick Start guide from Electron, we were left with a project that had an
index.html file in its root that to me seemed like something that should just run in the browser as well. And what do you know, it did. All we had to do was enable GitHub pages on our repository and add some DNS records to get it running on the ambassify domain. Et voila, the web version is live without writing an extra line of code.
Initially, I wanted to keep this as simple as possible as it is just a mirror app after all. But I’d like to invite everyone who reads this to contribute their own ideas to our repository. Even better, implement them and make a pull request.
Some useful and less useful ideas that popped into my head:
- Camera choice: most likely only useful on mobile phones that already have an easy camera app to use as a mirror but would still be nice
- Better permission handling: for the web, tell visitors why we will be requesting permission to use their camera before requesting it and show a nice message when permission was denied.
- A playback delay with sound so users can check out their audio quality (thanks to SustainedSuspense on Reddit.)
- Filters: add a party hat!
- Take a picture button
- Turn it into a talking mirror
- Two-way mirror (yes I’m a Harry Potter fan) to let you talk to other people through the mirror. Bonus points if you get it done without a server-component.