Hakuu in the making (2) — Mnjul’s Intimate Home vs-6517

Part of the post series for my process in making Hakuu.

The first version’s design, and technical experimentation/implementation

The Raining

Raining has been featured in Mnjul’s Intimate Home since over ten years ago, to convey eternal desolation, being influenced by Symphonic Rain (hey, a remake is available on Steam!)

For the previous versions of MIH, the rain was merely semi-transparent white lines moving vertically:

vs-3811: See the rain at the bottom-right?
Allodynia: rain path is randomly tilted; there’s also a periodic wind jittering effect

For Hakuu, I wanted something more realistic. I have seen WebGL doing crazy things, so it didn’t take me a long survey time until I found Codrop’s RainEffect, which used WebGL to simulate rain drop’s optical effect on a window with a distant background scene.

Here’s the question and challenge to myself: Can I apply such raining effect to a webpage with dynamic text & content, instead of a static image? And use webfont on that dynamic content? Would the result still be user interactable?

Implementing this raining effect proved to be the major challenge in launching Hakuu. In essence, to generate an image (actually a canvas) that Codrop’s rain engine can operate on, I needed to draw an entire webpage onto a canvas. Fortunately, I figured out I didn’t need to write every little canvas text & image drawing calls, as SVG has a <foreignObject> construct which I can put an entire HTML document inside. While there’re certain constraints, for a relatively static website as Hakuu, it suffices. I can use CSS, and I don’t have to care about manually laying out images, texts and other elements.

Yep, I said “in essence”, because that’s only the rendering part of the story. To make the rendered (and raining effect-applied) “webpage” behave like a normal, interactable one, and for scalability and an engineer’s sanity, here are some extra points I needed to take care of:

Height calculation for the rendered page

I needed to know what’s the dimension of the page SVG, such that further canvas processing could be made. Note: width of a page is determined by viewport width. Since the content is dynamic, I needed a way to calculate that, using JavaScript.

I invented something interesting: since my page is a SVG foreignObject HTML document, I’d just append that document’s container (the only child of its <body>) to the “canonical” DOM (the browser’s DOM), and use the regular getBoundingClientRect() operating on that canonical DOM to get the rendered height of the container. One function call and done. (Actually, it’s more than a function call, as I added a requestAnimationFrame() to make sure the browser had a chance for any reflowing.)

Interacting with links & other items

Since pages were now just images drawn to canvases, links from the original HTML were no longer active. It’s quite trivial to restore basic interactivity cues (like change mouse cursor) and actual click handling, though — just iterate through all link nodes in the page DOM, and during the height calculation stage, call getClientRects() to see where those link elements are, and on relevant mouse/touch events, see if the user is acting on the link elements.

If the link text color had to be changed with transition, some animation JavaScript would have to be employed. I’m not sure how this would turn out, though, as I’ll be redrawing the page canvas a lot. (By the way, currently, the page canvas is transformed to ImageBitmap where the interface is available, to reduce overhead.)

Images

…are not loaded/rendered in SVG foreignObject. So, I’d just get all image elements, AJAX-y loa them, and… here comes the ugly thing: I had to use base64 data URLs for the <img> src attribute. Unfortunately, blob URLs creating with the JavaScript running under the document DOM don’t work in the SVG DOM, and URL is a window property, not a per-document property, so there’s no way to generate blob URLs usable under the SVG DOM realm. So, base64 data URLs.

Fonts & how to share resources like pages’ CSS & AJAX-y Loading

I think by now you know this — webfonts are inserted using base64 data URLs 😉

Fonts and pages’ CSS are shared across pages and are AJAX-y loaded (By the way, can we not say “AJAX” anymore? Like, “fetch-y” loading resources?) after DOMContentLoaded, and are inserted into the page’s SVG foreignObject during rendering. Essentially, we have a big mess of asynchronous calls to deal with (fetch-y promises, createImageBitmap() promises, Image‘s onload callbacks, and requestAnimationFrame() callbacks, etc), and fortunately, ES8’s async-await construct lessened the burden a lot.

Scrolling

Well, after all those heavy topics above, how would scrolling be hard? But anyway, one thing worth mentioning — the page svg’s canvas/ImageBitmap is not where the rain effect is applied. That image has the full height of the entire page. However, since the viewport can be shorter than the page, only a portion of the page image is copied to the canvas that the rain engine operates on. Scrolling is just a parameter about where that copied portion is.

(This is done such that the rain drops would not be scrolled (i.e. you’re not moving your outlooking window), which I found to be less strainful for my eyes.)

displayAspectRatio and bypassing browser’s minimum font size limit

Any Hi-DPI rendering in that SVG is manually carried out. I only blindly enlarged everything by x times using CSS — this is achieved through combining css variables and replacing the variables when inserting the CSS into the SVG foreignObject. For example:

:root { --dppx: /*DPPX*/; }
html { font-size: calc(var(--dppx) * 16px) }
p { font-size: 1rem }

As we insert the CSS to the page SVG, we replace /*DPPX*/ with window.devicePixelRatio‘s value. By that, rem units will adjust accordingly, and the page will render window.devicePixelRatio‘s times larger.

Chrome imposes a minimum font size, and for Chinese (at least, Traditional Chinese) versions, it’s 12px. I hate that as it’s too large for UI elements [*]. Innovative way to bypass it is to set root element’s font-size to > 12px, like 16px or even 100px, and use rem elements. Chrome wouldn’t detect that and will happily render any font sizes as a result.

[*] On a non-HiDPI screen, 12px is barely the minimum for serif Traditional Chinese font’s legibility, I wouldn’t argue that. Actually, Micorosft’s PMingLiU switches to bitmap glyphs below 10px, essentially becoming sans-serif. I’m just ranting for designer’s freedom at the cost of usability 🙂

By now, I wish I had a beautiful diagram detailing all the loading & rendering pipeline for the page canvas with raining effect applied to show you readers, but… let’s give me a break.

In any way, I took the requirements one by one, and gradually converted Codrop’s sample code to something that fitted the design and engineering need of Hakuu:

(the order is incorrect, but you get the story.)

Iterating through the requirements also allowed me to check if applying the rain effect to a body of text paragraphs is delivering the effect I wanted; not the visual effect, but the psychological effect where the distorted text contributes to overall murkish atmosphere & tone, recurrently portrayed in Mnjul’s Intimate Home series.

Of course, distorted text is much less legible and readable, so an option to stop the rain effect is available in Hakuu.

Other visual design and UI elements, and responsive design

Regrettably, I didn’t have enough time to screenshot all the font/typographical design iterations for this blog post’s use.

Fonts – Fonts

Unconventionally I went with serif Chinese fonts this time. Let’s digress a bit and talk about serif Traditional Chinese fonts’ general situation in Taiwan a bit…

Serif Chinese fonts for display on computer screens have been a repeatedly debated topic in the past decades, primarily due to (1) the uncommendable design choices in Microsoft’s PMingLiU family; (2) PMingLiu’s bundling as default system font in Windows in Taiwan, and as a result, its epidemic usage (even when using a serif font would obviously be distasteful); (3) people’s equating “serif Chinese font” with stereotype about PMingLiU, and (4) that few (if any) foundries in Taiwan were capable of covering all Traditional Chinese characters with fonts that conform to Unicode’s spec and enough OpenType features; and for those who were, they focused more on sans-serif fonts than serif fonts (indeed, serif Chinese fonts inherently have less possible artistic/design variations unlike Japanese kanas or Latin1 characters), and regardless which, most of the fonts were geared toward utilization in visual-heavy scenarios such as banners & posters, instead of main body text (and fonts suitable for main body text is what I’m looking for to use in Hakuu).

In prior versions of Mnjul’s Intimate Home vs-3811, and in even prior versions of MIH, I eventually opted to use Kozuka Gothic Pro and Meiryo, two sans-serif Japanese fonts that covered enough Traditional Chinese characters (or Kanji that I could substitute with).

Well, that’s until Google and Adobe announced Noto Sans CJK / Source Han Sans. Their announcement was exciting to me: I trust Google to employ all OpenType/Unicode capabilities (such as ideograph variations, even though on web it’s hard to trigger them), and I trust Adobe as a foundry. I immediately switched to use Source Han Sans for vs-3811.

For Hakuu, I’ve been pondering a visual design that mimics printed publication, such as books and magazines. Printed publications use serif fonts for legibility and readability. And I’ve always had my habits to use light weights (300 is a norm for me), which few serif CJK fonts provided. What could I do?

As I was pouring ideas into XMind and Adobe XD, coincidentally, Google and Adobe announced Noto Serif CJK / Source Han Serif. I immediately switched my fonts for the mockups and thankfully, the fonts look awesome! (Old Adobe XD mockups I’m showing in this series of posts have Hiragino Mincho.)

Uhm, did I skip talking about the English (i.e. Latin1) font I use? After surveying and testing a bit, old-style typefaces still looked the best, and I collected quite a few Garamond-derived fonts. While old-style figures did’t go well with CJK characters [*], fortunately, at the end of week-long search, Cormorant Infant by Christian Thalmann went enough well with Source Han Serif.

As a side-note, for Hakuu and probably moving forward I try to use free-to-use fonts. I’ve always stolen fonts in previous versions of MIH… and I never loved doing so. For Allodynia, I stole STFangSong (华文仿宋) from Apple, and Gabriola from Microsoft.

[*] Gabriola also had old-style figures, but it had “regular-size” figures sitting in other codepoints, so I used a font editor to swap the glyphs and everything was good.

Let me digress one more time, about Source Han Serif. Frankly, I love the CJK characters from the Japanese variant of the font, but I want punctuations to be in Taiwan’s Traditional Chinese style. Unfortunately, as I said before, there’s no easy way to switch ideograph variations on web, so I had to serve two Source Han Serif webfonts… except that I couldn’t. Chrome wouldn’t render any text at all in the SVG foreignObject on canvas if two Source Han Serif fonts were served simultaneously :(.

Fonts – Typography

Unleashing full typographical designs is challenging on a pure-web implementation. It’s now even more challenging with my raining set-up — now that things are rendered in a SVG foreignObject, it’s nearly impossible to do anything through JavaScript to implement (“mimic” would be a better word, for sure) typographical designs.

With that said, most of the typography-related priorities might be solvable with CSS with a font design tool, though. (I’m talking about the Cormorant Infant’s x-height, kerning, and the general em size such that it goes better with Source Han Serif. Let’s see what I can brainstorm in the future.

UI

Even as a static website, Mnjul’s Intimate Home usually have a couple non-navigational UI elements users can interact with; one for controlling the raining, the other for controlling the background music. In the past versions, I mostly use graphical elements for those:

vs-3811’s volume control, with a stolen speaker icon from Windows Vista; rain intensity tied to background music volume
vs-3120’s volume controls at bottom-right, one for background music, one for raining (which controls rain intensity too)

I transitioned to semi-text-only UI for Allodynia:

Allodynia’s almost text-only background music volume control (I regard + and – as iconography)

And I decided to fully do away with iconography in Hakuu at all. In reality, for background music control [*], even if I use only one character for each of predefined 6 volume levels [靜 微 小 中 大 吵] without any context (like a title for this group of UI elements), it’s still self-evident that this six characters are intended to control audio, as 靜 and 吵 are adjectives for sound. In English, while it’s also possible to write “Silent”, “Low”, “Medium”, “Loud”, the varied word lengths would greatly impact visual rhythm.

Mockup (with Adobe XD) for Hakuu’s background music volume control

In all, the minimalist, text-only design for raining control and volume control is clear and intuitive.

[*] I haven’t composed the background music for Hakuu, so background music control elements are only living in my Adobe XD files.

Responsive Design

Mnjul’s Intimate Home has never seen responsive web design (or, I should say “mobile friendly” to be exact). I never felt a mobile phone’s viewport was suitable for the amount of content; a narrowed-down viewport was also inadequate in delivering the desolate atmosphere. I’ve always argued that Mnjul’s Intimate Home is just intrinsically and inherently not for consumption on small viewports, but then again, I wanted to give myself a challenge (and potentially showcase my technical abilities to anyone who came across my website with a mobile phone), so I still made the site fully responsive.

Well, to be fair, as the first version had only text paragraphs, that’s actually a walk in a park (though, technically, at JavaScript side I still have duplicated switching logic to decide current viewport layout, which I plan to refactor only after I have more complex content layout.) Quite fortunately, the entire raining effect is available on all most-recent Android and iOS browsers, with no code modification needed at all, and I observed pretty snappy frame rate and UI on my iPhone 6 and Nexus 5.

發表迴響

你的電子郵件位址並不會被公開。 必要欄位標記為 *