lch361.net

Author

lch361

Version

Rolling release

License

CC-BY-ND-4.0

History of development

I have been a software developer for a long time already, but I haven’t always had my own website. A year ago, when I had developed half of my projects already (lcalc, FCDL and stm32-morse-device), I recognised a problem: nobody knew about my achievements, my projects nor my knowledge. I simply didn’t have familiar people that were as competent in computers and development as me. Besides, my projects always could use a little bit more of popularity.

That’s when the idea to develop my own website was formed. First of all, I just wanted to write down my skills and projects in this website, just so people would know that a genius programmer’s behind all this. Also, I thought it would be nice to aggregate all of my projects and contact info in one place, in my own little corner of the Internet.

And so, initial version of lch361.net was published. It is not being hosted today, because new version replaced it, but here is an old source code, still available for building and viewing for those who are interested. In my opinion, that website had some major drawbacks:

  • Navigating projects was hard. They were strictly divided by 5 categories, there was no total list of every project, so it was harder to find specific projects by their name.

  • Also, because projects were only divided by categories, it was hard to expand for the new projects. What happens if I start exploring and boldly coding in entirely new area that was never tried by me? I would have to draw a new icon, write new description for new category, and then put there my first project.

  • In general, I wasn’t really satisfied with the information presented on my website. I always felt like I have far more stories to tell, more knowledge to share. Aggregating the projects is simple, but I always knew about every project a little bit more than shown in their respective homepages or READMEs. So why not try to write proper articles next time?

  • The style of that website wasn’t bad, but I recognized that I could do it better.

With these reasons in mind, the idea to develop lch361.net 2.0 was formed. This time, I wanted to not only present my projects and skills, but also write articles about advanced topics, sharing with visitors the most valuable thing in software development — knowledge. Along the way, I also was experimenting with style and interface, improving it for readability, navigation and overall making it more interesting.

This is how lch361.net in its current state was published. There is no stable release for this website, because it is constantly improving, just like me. Today, I am very proud of this work and I’m looking forward to maintain this website for a long time.

Architecture

Website is divided into 5 sections, each of them consists of either one page or a list of pages.

  • Home — main page of the website. Briefly describes this website’s idea, tells about me and mentions every other section.

  • FAQ (new) — trivial questions that someone might want to ask me.

  • Projects — collection of all of my developments, with various links. Migrated from old website, enhanced with detailed descriptions for every listed project.

  • Articles (new) — technical writings that are not tied to one of my project, although articles and project descriptions can depend on each other.

  • Presentations (todo) — in the future, when I’ll be making technical presentations and speaking on conferences, then I’ll probably make a new section where these presentations will be archived.

Together, they form the following net:

Net diagram of my digital homeworld. Arrows represent references between sections

My website is also designed with one core principle: no JavaScript. There is no interactivity on my websites, just plain HTML & CSS. I don’t think JavaScript is a good idea in general, my website doesn’t need interactivity, and worst of all — I wasn’t going to have performance issues on my website. Every page should load near–instantly (so no JavaScript UI frameworks) and should not be CPU–heavy when viewing (no JavaScript/SVG animations either).

Websites shouldn’t be complicated. They were designed to be simple. Although the majority of the Internet websites nowadays might not agree with me.

Rewriting from Hugo to Zola

Old and new versions have one crucial difference: old website was written with Hugo, new website is being developed with Zola.

First of all, Hugo and Zola are pretty similar:

  • both are command line programs;

  • both are static site generators (further, SSG);

  • both have extremely fast times (around a few milliseconds) to build a website and extensively use caching;

  • both can serve the website while rebuilding it on any change;

  • both are using Markdown with shortcodes for content and Sass for stylesheets;

However, there are also some crucial differences between these. Feel free to check Hugo and Zola documentation to see the differences and choose your favourite flavor of SSG12. You can also look into the new and old source code of my website to see practical differences in implementing the website with both SSGs. The table below is based on the documentation of SSGs paired with my experience of development two versions of the website.

Comparison of Hugo and Zola by specific criterias
CriteriaHugoZola
Inserting template instructions in HTMLUses {{ and }} to insert both template expressions and statements, making the syntax simpler.Uses {{ and }} to insert template expressions and {% and %} to insert statements, visually separating template instructions that produce content from those that do not.
Control structuresHas if and for loops. Also has a very convenient with keyword, allowing to evaluate an expression with certain context with a very compact syntax.Has if and for loops. Doesn’t have a with keyword, forcing the usage of if instructions, which makes the syntax simpler.
ImplicitnessMany things in Hugo’s functionality are implicit. For example: layouts for every page follow a strict order of lookup; context . can have different value in different parts of the program; etc.Many things in Zola are explicit. For example: templates for every page can be set explicitly in the frontmatter; template context never changes when evaluating the template; etc.
Amount of functionalityCan do a ridiculous amount of things not only related to website building: everything that Zola can + Git, other markup languages and many other things.Can do a little amount of things just needed for creating a website: Markdown, HTML, CSS/Sass and static assets.
DocumentationExtremely large due to numerous features Hugo has. It’s better to use a built–in search index if you need to find something, especially if you want to develop the website quickly.Small and simple, just as Zola itself. The search index is also present, but you can easily scroll through every chapter in table of contents and figure out how the entire SSG works.
Site configurationVery complex. The documentation for simple hugo.toml file consists of numerous chapters.Simple. The documentation for simple config.toml file consists of few chapters.

In summary, I personally prefer Zola due to its minimalistic and explicit nature. It was far more intuitive to develop the website with Zola, and I do not regret a single line of HTML written in Zola instead of Hugo. Unless…

Extending the minimalistic functionality of Zola

As I was saying, Zola is a minimalistic SSG. It has just enough features to develop a website with static content, and nothing more. However, there are some interesting ways to add new functionality to site generation process, which the next subsections are all about.

If Zola doesn’t have the piece of functionality you want, it’s not a reason to give up on your project; it’s the reason to get creative.

Collecting Git information

Zola doesn’t have, for example, GitInfo like in Hugo — an object that has an information about last Git commit that modified a certain page1. Unfortunate, because it would make implementation of Git commits for all pages on my website straightforward.

On the other hand, Zola has one mighty function — load_data. That’s right, Zola is capable of reading external input from files, and even supports some common data formats, like JSON.

Lastly, we can always resort to generate the input for load_data via scripts or external programs. In my project, I used Python scripts all the way, because it’s still my favourite scripting language for general purpose. My task was to write a script that would present Git commit info in a JSON.

Thus, if we define script.py as our hypothetical script for this task, and data.json as its output, data should flow in our build process like this:

Data flow diagram for Git info collecting script and Zola

That’s how the update_times.py was developed. On execution, it puts its output into a file inside the resources folder — a folder generated specifically for temporary input/output between Zola and Python scripts. According to the data flow diagram above, update_times.py runs before zola build.

There weren’t any further modifications to my build system. When developing, I was simply executing update_times.py before every zola serve command and it was enough to implement Git info for my pages and to develop further content comfortly. Until…

Custom markup language for math formulas

There is one difficult task in generating webpages: rendering math formulas. I am very unlucky, considering that I needed to write math formulas on my first article.

If you try to search the Internet for a way to render math in HTML, chances are you’ll find MathJax. The good part is that it is:

Beautiful and accessible math in all browsers

The bad part is:

A JavaScript display engine for mathematics that works in all browsers.

JavaScript is forbidden in my website, so I didn’t want to use MathJax and similar technologies anymore.

Reading the web documentation, I found MathML3. Now this was a very promising feature of the web. MathML is also an XML–based markup language, and in this regard, you can compare it to SVG: MathML to mathematical formulas is the same as SVG to images.

So I immediately started using MathML in my first article. However, I recognized that it was not a good idea to write MathML by myself:

MathML written for one of the formulas in my first article
<math display="block"><mrow>
	<mi>memmove</mi><mo>(</mo><mi>k</mi><mo>)</mo>
	<mo>=</mo>
	<mfrac>
		<mrow><mi>k</mi><mo>*</mo><mn>2</mn><mi>m</mi></mrow>
		<mrow><mi>s</mi></mrow>
	</mfrac>
</mrow></math>

The HTML markup above will be rendered in the following formula:

memmove(k)=k*2ms\text{ memmove}(k) = \frac{k \ast 2m}{s}

Just like it’s usually not a good idea to write SVG by hand, MathML also shouldn’t be written, but rather generated from some sort of markup language.

When I was looking for alternative markup languages for math notation, my first choice was Typst’s math mode4. At that time I was writing a lot of documents in Typst, and it was my favourite technology for typography ever. Particularly, I enjoyed how easy and intuitive it was to write formulas in Typst, so I immediately got an idea to try to implement that part of Typst in my website.

To do that, I was planning to use the same power of Python as with Git info, but the implementation would heavily differ: Git info could be calculated for all files ahead of compilation pretty easily, while formulas are engraved inside the content pages, and parsing them out and calculating every formula would be far beyond reasonable difficulty. For the first time, I needed to generate content externally not before, but along the Zola build process.

And so, I consulted the Zola documentation once more2. Looked again at the load_data documentation and noticed that, aside from the path parameter, this function has a url parameter. That’s right, this amazing function can read data from external URLs, and for one URL, a response may be generated dynamically (contrary to static content, where all data is on the disk before site compilation). This is excellent, because it allows us to transform pieces of content found while parsing Markdown pages. On this principle, I started implementing a solution for substituting Typst formulas inside every math shortcode to MathML.

I implemented a second Python script — an HTTP server that for every GET request responses with converting URL path into MathML. For conversion itself, Pandoc was used — a very powerful utility for converting documents between a myriad of formats. My server processes every GET request in the following fashion:

GET request flowchart for a second Python script

The only problem left to be dealt with was connecting to the HTTP server while evaluating Zola templates.

  1. HTTP server listens on a random free port (binding to a port 0 gives this effect), so we can’t predict the URL for server ahead of compilation for Zola to use.

    Solution: on server initialization, its URL with a port number is written out into a file that Zola will pick up with a good old load_data function.

  2. Sending arbitrary content taken from Markdown pages via URL paths was deemed unsafe, so URL–safe base64 is used for transferring the data5.

    Solution: Zola actually has base64_encode function that will help us in this task2. I didn’t see any notes in the documentation stating that it is a URL–safe base64, but in any case, we can always replace + with -, and / with _6.

All problems have been solved, so the server and zola build just need to be launched in the following fashion:

Data flow diagram between Zola and second Python script

Wrapping Zola in Python extensions

When I developed the first script, building my project was still fairly easy: just run one script, and then zola build.

When I made the second script, building the website got a little cumbersome: I needed to always run first script, then open a separate terminal specifically to run Python HTTP server, and then run zola build.

This is why I made a Python wrapper around Zola: to make the build process once again as simple as calling zola itself while still having my extensions of functionality.

It goes without saying that this idea works well, but it of course has major drawback for my project: performance degrades. Remember how I said that Zola can build a website in milliseconds? This is no longer the case, because, at least on my machine, the build time for my website increased to 1.5s (when using zola serve, subsequent rebuilds take ~30ms though due to caching). Probably, this is because:

  • Python is not the fastest interpreted language;
  • calling pandoc on every Typst conversion is kinda expensive;
  • connecting to a server and exchanging data via HTTP also is an overhead.

I think this problem can be solved by forking Zola and extending its Tera, the template engine. But I don’t really want to do that, because it would take away my beloved minimalism from Zola.

Conclusion

Developing lch361.net was an extremely interesting and experimental process, just as I like to develop software.

This article shows an unusual, creative and even quite fast (in terms of development time) way to extend the functionality of a compiled software with the power of networking and a couple of lines of Python. Extending functionality this way is the main reason why I like minimal and composable programs.

References