I recently made a simple website where you can answer this exact question.
For those who aren’t nose deep into programming, the simple overview is that this is saucify. You should go there to convert your centimetres or inches to sauces. It’s intended as a silly way to count how many sauces tall your cat is… It’s hard to explain, take a look at this meme and you will probably get it –
Using my site, we can immediately determine that this cat has the length of 0.328 borzoi’s noses, or 0.102 Meters or 10.16 centimetres, … etc. You can also determine the reverse – if your cat is 15cm long and ask “How many sauces is that?” You can type into the centimetres box and discover that your feline is exactly 2.953 sauces long. You will now know the poor thing is just shy of 3 sauces long.
From a dev perspective, I want talk about it and explain what it all means, how I got there, what I learnt and what I am considering moving forward.
The source code of saucify is available at the saucify GitHub repository. There is currently no specified licence, if you would like there to be an MIT one, please contact me to express interest and I’ll sort it out. Currently, one should assume this work is copyrighted with no permissions granted.
With that boring bit aside, you may be surprised to learn this is in plain JavaScript, Html and CSS. It is the 2nd major iteration of the project. `NPM` tooling was retrofitted to the source code towards the end, to make it seamless to deploy with minified JS and CSS. It is tested with `bun` in wsl, and testing is done in bun due to its percievieably much faster `package.json` script run times.
The program has event listeners on all the fields and buttons. Each time a field is changed (or a submit button is clicked), the JavaScript calculates the value in sauces and then iterates through all the other fields and recalculates them. The architectural decision of having a central unit to convert to and from was a great improvement over the prototype; the naive approach I first found was to have conversion factors to go to and from every single other field. Moving to the new approach, allowed me to abstract away many details behind a single linear algorithm. This abstraction made it very easy to add a new unit at any time, only requiring 2 very simple conversion functions to convert to every other unit listed. If it wasn’t for this decision, the code would have kept growing exponentially in complexity each time I added a new unit as it would have required retrofitting new units’ conversion factors everywhere.
This project has enlightened to the fact that plain JavaScript is cumbersome to maintain in even small-sized projects. `script.js` contains countless flaws and room for improvement. My project would be improved greatly with more rigid encapsulation of state. Currently, the script requires several steps of boilerplate across several files to add a new unit type, despite the fact I am not benefitting from the extra control boilerplate often offers:
- add a handful of lines to the html, making careful note of the id used for a button and id used for a input field
- make sure the new elements have the same CSS classes as the ones before it
- add some constants at the top of the JavaScript, for handles to the 2 ids created. They must match exactly across files.
- add the mentioned handles to an array
- add a conversion constant
- add conversion functions
- add the conversion functions to the array
None of these steps are checked in any way, it’s 100% possible to write and deploy broken JavaScript that won’t work on any browser. The only way to prevent this is to constantly manually test the website in several ways, after making changes. This too is very error prone, and one factor is that browser caching can hide changes as you make them. There are workarounds, like shift+F5 in Chrome, but they are also error prone and manual. Better yet, it requires frequent unnecessary context switches which is hard for humans to get right every time.
Another factor is that JavaScript as a language is in itself very flawed, with no type safety and often lacks behaviour that even the extremely flawed C standard library has (eg: formatting a number to a very long string of digits correctly, without switching to scientific notation or incurring unwanted rounding). This is not to say JavaScript was the wrong language to prototype this idea in with my limited knowledge, but I recognise the vast room for improvement and I now understand why frameworks have become some popular in the Web world.
It would be nice if all of these details were encapsulated into one place and had a builder class, function, etc that made all the necessary changes to the DOM and kept track of global state as required. It would also be nice to try out a “decimal fixed point” library to make the unit conversions far less error prone at the extremes (eg: converting from Planck length’s to sauces often just calculates `0`). That would be less performant, but it would be more accurate and my script currently has minute overhead in chrome compared to the rest of the browser’s overhead so it is unlikely to cause an issue. While the conversions are approximately correct when one considers the vast change of scale here, it looks stupid and makes the conversion not 100% reversible as intended. Another minute decision I made and regret was using the `.value` field of the text boxes to always store the conversion values, while it works most of the time, it leads to precision and localisation problems. Next time I will be storing the numbers separately from the end user’s display so I can make rounding decisions for the end user without ruining the accuracy of the entire system.
I now deeply understand now why frameworks have a place in the industry. All in all, I had a great time making this silly tool and learnt so much. I learnt how rough but useful JavaScript tooling is, how useful JavaScript is for prototyping, how limited it can be, how the front-end can be hard and sometimes interesting, why static websites are very unpopular for devs, and also why anyone would want a framework in the first place. I look forward to learning more.