View Website: https://underwaterdictionary.com/
This pet project was inspired during a trip through southeast Asia in 2017. In the world of scuba, underwater communication is done using hand signals, similar to sign languages for those who cannot hear. During a scuba trip in Malaysia, my wife and I were confused by a hand signal that our dive master gave us. We also learned that Thai people will text each other "5555" in response to a joke, owing to that fact that the Thai word for "five" is "ha", which led us to start flashing all five fingers on a hand repeatedly to express laughter underwater.
We marveled at the variety of hand signals used throughout the world, and I went about looking for an online resource that I could search through to learn more about the signals in the regions we were traveling. No such service existed - only flashcard-like resources with the standard, static set of signals. The data models weren't too complicated, so I decided I would build a searchable directory myself on my spare time. I decided that the best way to convey the signals would be images and animated GIFs or short videos for signals that include motion.
The backbone of the system is a LAMP stack consisting of Linux, Apache+nginx, MySQL, and PHP. I used the Laravel framework for PHP. Composer, npm, and bower manage dependencies, and I sources several manually-installed packages that perform the heavy lifting of image and video conversion (ImageMagick, Gifsicle, JpegOptim, Pngquant, and Optipng). It is hosted on our servers here at Complete Code Solutions.
Since we are not hiring a team of copywriters, in order for this service to be feasible the database needs to be able to be contributed to by any scuba diver with a camera. This opens us up to a whole realm of spam vulnerability, as well as the need for images and video conversion automation. To solve the problem of blocking spam, the system simply requires review & approval from an admin before content is displayed on the site. Relevant models all have an `approved_at` column, and I extended the Laravel Query Builder model to add methods `withUnapproved()` and `onlyUnapproved()`, similar to the soft delete `withTrashed()` method.
The meat and bones of this service is a database of "phrases" and "signals", each of which has one or more images and/or videos describing them. Since we can't expect that every scuba diver out there is also an AV nerd, we needed to make sure that it was easy to upload images & videos, but also process them before adding them to the site to ensure quality, proper formatting, and file size optimization. In order to accomplish this, I wrote some command line scripts that utilize the image & video conversion packages listed above in order to create optimized versions of videos & images, and pull a static frame out of animated GIFs for display on page-load.
Since this site delivers a potentially large amount of graphical content, bandwidth and load time became a concern early on. If we were to just dump a huge list of animated GIFs and videos to the browser window we would surely face speed issues. Instead, I leveraged the gifplayer package to display the static frames generated by the automatic image converter on page load, and load the full GIF or video only once the user clicks on an image. This package did a lot of what was needed out-of-the-box, but I added functionality in the form of a few pull requests in order to get it do everything we needed.
Since the target viewers (and contributors) are all around the world, it was important that the interface and data models were built with translation and internationalization in mind from the start. Though as of the time of this writing the site is only available in English, all of the interface elements and all of the relevant data models are i18n-enabled, so that translations can be added for any language at some time down the road. Two packages were crucial in setting this up: laravel-gettext and laravel-translatable.
Though typically user-contributed uploads like images & videos are not tracked in a project's version history, I felt it was important to include the initial batch of content that we created for launch. iseed is a handy little package that creates Laravel seed files from existing database data, but it does nothing with image references. I added this functionality to the package and created a few pull requests. The images PR was not merged in by the package maintainer, as he felt it exceeded the intended domain of the package. Perhaps I will release this feature as a separate package, but for now it is available on the seed-images branch of my fork on github.
The initial site and server was always intended to be the API for future clients. I had some fun putting together a GraphQL API to facilitate this vision. The plan is to eventually develop native app clients which consume the API. A major upside to doing this will be the ability to download & cache phrases and signals to your device while within internet coverage, and be able to view them later while not. Since, after all, you're not always on 5G when you're traveling the world and scuba diving.
As you can see, there is very little activity or traffic on the site currently. The hope is to someday spread word about the site amongst scuba circles, but I have not found any time to devote to that as of yet. If you're a scuba diver and you're reading this, consider recording and uploading some animated GIFs!