1
2
Fork
You've already forked colors
0
  • Fennel 88.4%
  • CSS 8.5%
  • JavaScript 3.1%
2026年04月10日 20:58:31 -04:00
assets just saving 2025年07月08日 21:48:36 -04:00
libs Upgrade RetroV 2025年08月18日 20:32:47 -04:00
lua-color @ac886a1192 Initial commit 2025年07月08日 16:26:22 -04:00
.gitignore Move icons to a different file since it's kinda messy 2025年07月17日 13:50:37 -04:00
.gitmodules Initial commit 2025年07月08日 16:26:22 -04:00
banner.png Add a banner 2025年10月03日 19:05:02 -04:00
build-helpers.fnl Ignore hidden files created by Emacs 2025年07月21日 13:48:18 -04:00
build.fnl Separate files 2026年02月15日 21:11:59 -05:00
html.fnl Add donation button 2025年10月07日 13:20:02 -04:00
i18n.fnl Detect preferred language 2026年03月12日 14:39:01 -04:00
icon.png Use an icon that's more representative of the app 2025年09月17日 21:31:24 -04:00
icons.fnl Add donation button 2025年10月07日 13:20:02 -04:00
index.fnl Separate files 2026年02月15日 21:11:59 -05:00
index.js We can now delete saved colors 2025年07月21日 15:25:38 -04:00
itch.png Add itch.io badge 2025年07月14日 12:08:04 -04:00
LICENSE Add license 2025年07月08日 16:28:21 -04:00
main.fnl Detect preferred language 2026年03月12日 14:39:01 -04:00
manifest.toml icon and manifest 2025年07月09日 21:51:05 -04:00
README.md No AI 2026年04月10日 20:58:31 -04:00
style.css Add donation button 2025年10月07日 13:20:02 -04:00
webxdc.js Initial implementation of saving 2025年07月12日 18:55:55 -04:00

Screenshots of So Many Colors

🪻 So Many Colors!

So Many Colors! is an app that picks a random base color and derives color schemes from it. It helps you find color combinations that you can use for your designs and artwork. It's a Webxdc app that's meant to run on Delta Chat, monocles chat, and Cheogram.

You can get it on itch.io, webxdc.org, or in the release page!

Available on itch.io button

License

This is anti-capitalist software, released for free use by individuals and organizations that do not operate by capitalist principles.

Contains bits and pieces of the following projects:

Please support them if you can!

  • Fennel: Programming language that runs on the Lua runtime (MIT License) (version 1.5.3)
  • Fengari: Lua VM written in JS (MIT License) (version v0.1.4)
  • Shelf: Remarkably small, remarkably useful CRDT (MIT License) (commit efacd67)
  • Pico CSS: A minimalist and lightweight starter kit (MIT License) (version v2.1.1)
  • Solar icon set (CC BY 4.0 License)
  • RetroV: Virtual DOM rendering library (MIT License) (commit 3fed42e)
  • lua-color: Library for manipulating colors (MIT License) (version v1.2-4)
  • Wikipedia (CC BY-SA 4.0 License)

Introduction for devs

This app is written in Fennel which is compiled into Lua which then runs on Fengari which is a Lua VM written in JavaScript. Now why are we standing on the computer language equivalent of the Tower of Babel? Because it's fun to write in Fennel and I wanted it to run on the browser!

How to build

Prerequisites

Ok this is kind of an annoying part, but bear with me. We need to get you some of the tools that we need to build this project.

  • Lua is used to run the Fennel compiler. Since Fengari is compatible with Lua 5.3, we should install 5.3 on our system.
  • Fennel is used to compile our code into Lua.
  • LuaFileSystem is used to check for any file changes.
  • luaposix is used to pause the program while waiting for changes.
  • Zip compresses our app and creates the .xdc file.

Installing these tools vary between different operating systems (there's so many of them!), but I've tried it on the following systems. Here's how you can install Lua and its dependencies:

Debian-based (Linux Mint, Ubuntu, etc.)
Debian

Install Lua, the Lua libraries, and zip.

$ sudo apt install lua5.3 lua-filesystem zip lua-posix

After that, install the Fennel executable.

OpenBSD
OpenBSD

Thankfully, everything we need are a part of the package manager!

$ doas pkg_add fennel-lua53 lua53fs lua53posix zip

Building

So after installing those tools, what we need to do to build is to cd into the directory and run:

$ fennel build.fnl

This compiles our Fennel program into Lua and bundles everything else into an index.html file. There's nothing else to do after that, really! You'll find that there's a dist/moments.xdc file that's also ready to go. Drag and drop that file into Delta Chat, and you're done! You can also view the app by opening index.html in your browser.


Technical deets

This is a deep dive into the inner workings of the app. This is mostly for me because I'm likely to forget how this app works in the future, but if something is not clear let me know!

Useful hacking tools

Before I was using Sublime Text with sublime-text-fennel, but I find that it's much nicer to use Emacs with fennel-mode and rainbow-delimiters when working with Fennel. It takes some getting used to, but it feels worth it.

It's also good to have the fennel executable in your path so that you can use M-x fennel-repl within Emacs as well.

Build process

Let's start with the fennel build.fnl file. This runs on the Lua runtime on your machine and it generates a single index.html file that includes:

  • Fennel files compiled to Lua in a <script> tag.
  • JS added into a <script> tags.
  • CSS added into <style> tags.

The HTML is represented as a Lua table in index.fnl which build.fnl reads and writes an HTML string into index.html. Inside the Lua table you'll find a function call to build.load-assets that's defined in build-helpers.fnl. This takes a sequential table (array in other languages) which has the files that we want to include, the filetype, and whether we want to inline it or not.

(fn load-assets[assets](table.unpack(icollect [_[assetfiletypeinline](ipairs assets)](if inline(casefiletype:js[:script{}(read-fileasset)]:css[:style{}(read-fileasset)]:lua[:script{:type"application/lua"}(read-fileasset)])(casefiletype:js[:script{:srcasset:type"text/javascript"}]:css[:link{:rel"stylesheet":hrefasset:type"text/css"}]:lua[:script{:srcasset:type"application/lua":asynctrue}])))))

I prefer to inline everything to have a nice single index.html output, but there's no technical reason for that I just like it that way. You'll notice though that webxdc.js is not inlined, however. This is because Delta Chat (and other Webxdc compatible clients) provide this file themselves so we don't actually want to bundle the webxdc.js file that's inside our repository.

It also reads and compiles our Fennel files into Lua which is written inside index.html and also main.lua. The files that we want to build are in the files table. Finally, it creates a zip file called dist/colors.xdc which we can then share with other people.

;; Create our webxdc app(let [commands["mkdir -p dist""rm -rf dist/colors.xdc""zip -9 --recurse-paths dist/colors.xdc index.html icon.jpg manifest.toml"]](each [_cmd(ipairs commands)](print "Command"cmd)(os.execute cmd)))

Additionally, build.fnl also watches for file changes and re-runs the whole build process automatically! To quit, simply press Ctrl+C.

How the app works

index.js

The story of our program begins at index.js. This initializes our local "database" which is stored in window.crdt and basically manages sending our data through webxdc.sendUpdate and receiving our data through webxdc.setUpdateListener and keeps the database (which is a CRDT) synced as it sends and receives.

main.fnl

Most of the logic of the app resides in the Fennel files and it all happens in main.fnl. This file is responsible for displaying the UI, generating random colors, converting colors to different formats, and saving them.

html.fnl

This file converts a Lua table that represents our HTML and converts it into virtual DOM that's handled by RetroV. Calling render checks for any changes in our data is then the difference is displayed by this library.

icons.fnl

All our SVG icons reside in this file.

i18n.fnl

Translation strings are stored in this file. The current locale is stored in localStorage, and it uses the locale key to use the right strings. If a key-value is missing, it falls back to en locale.

No AI

No AI was involved in making this. Fuck that.