<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>liquidx</title>
    <link>https://liquidx.net</link>
    <description>Alastair Tse</description>
    <atom:link href="https://liquidx.net/feed" rel="self" type="application/rss+xml" />
    
      <item>
        <title>Train Lines</title>
        <link>https://liquidx.net/posts/2026/train-lines</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2026/train-lines</guid>
        <pubDate>Fri, 22 May 2026 00:00:00 GMT</pubDate>
        <description><![CDATA[<p>I decided to revisit an old project of mine, rendering train lines in Japan. I gave a talk at Tokyo Tech Meetup about my obsession with open geo data and trains. Aside, I'm hosting a <a href="https://luma.com/e9g4xnxe">Geo Tokyo meetup</a> next Wednesday, join me!</p>
<p>So with the help of Claude Code, Antigravity and Codex with a lot of nudging, I made a huge revision to the <a href="https://train-lines.liquidx.net">train lines demo</a> that works on mobile, shows stations and train colors, etc.</p>
<p><img src="https://content.liquidx.net/media/posts/2026-05-22-train-lines-f5dc5887.png" alt="Screenshot_20260522-163100.png"></p>]]></description>
      </item>
      <item>
        <title>LLM Navigator</title>
        <link>https://liquidx.net/posts/2026/llm-navigator</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2026/llm-navigator</guid>
        <pubDate>Tue, 19 May 2026 00:00:00 GMT</pubDate>
        <description><![CDATA[<p>I had an idea about creating a browser that doesn't use the internet.</p>
<p>What does that even mean? The idea is seeing whether you could get an AI to dream up a webpage given the URL, and then make that operate like a real web browser.</p>
<p>At the <a href="https://worldwide-hackathon.mistral.ai/">Tokyo Mistral AI Worldwide Hackathon</a>, I revisited this idea and built a version of this. It's now on my vibe-coding sandbox <a href="https://aversion.one/">v1</a> as <a href="https://aversion.one/of/llmavigator">LLM Navigator</a>.</p>
<h2>How it works</h2>
<p>When a URL is entered, a prompt is given to the LLM to generate the HTML and CSS for the web page. It is also fed some context like whether the new URL is coming from a click from a previous page (in which the previous page is also fed in as context).</p>
<p>One other little trick is images. I had tried using the image generation models but it soon blew through all my credits. So instead, it does a separate image generation pass but mainly sticks to SVG for the images to keep speed high and costs low.</p>
<h2>Examples</h2>
<p>If you give it a fictional twitter account, it will try to generate what it would look like. Imagine if William Shakespeare had a twitter account:</p>
<p><img src="https://content.liquidx.net/media/posts/2026-05-19-llm-navigator-b3f3bb31.png" alt="Screenshot 2026-05-19 at 12.46.28.png"></p>
<p>Rather than be asking the LLM "what do you know about this?", things can get an bit interesting if it become free form in HTML. It could also make up the year it wants to respond in.</p>
<p><img src="https://content.liquidx.net/media/posts/2026-05-19-llm-navigator-7d776d3a.png" alt="Screenshot 2026-05-19 at 12.49.32.png"></p>]]></description>
      </item>
      <item>
        <title>Publishing System</title>
        <link>https://liquidx.net/posts/2026/publishing</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2026/publishing</guid>
        <pubDate>Mon, 18 May 2026 00:00:00 GMT</pubDate>
        <description><![CDATA[<p>I'm making some big changes to how the internals of my blog works. Everything is driven out of an Obsidian Vault with some custom tools (partially vibe coded) to do monitoring and publishing of the updated content.</p>
<p>Once a change has been detected, it triggers a regeneration of the content archive and then triggers a re-deployment of the website.</p>
<p>This post is half a test post, half explaining.</p>]]></description>
      </item>
      <item>
        <title>Dishwasher</title>
        <link>https://liquidx.net/posts/2026/dishwasher</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2026/dishwasher</guid>
        <pubDate>Sun, 17 May 2026 00:00:00 GMT</pubDate>
        <description><![CDATA[<p>Using AI coding agents is a little like using a dishwasher.</p>
<p>There's no proper way to load the dishwasher, like there's no proper way to really prompt coding agents. People give you advice, say it works for them, but every set of dishes are unique, just like every code base is unique.</p>
<p>When it finishes, you need to check the dishes to makes sure it really is clean. Sometimes an odd dish here or there is dirty and you don't notice. You put it away dirty. You ignore that it got 95% of the dishes clean, it's still not satisfying.</p>
<p>Same with agents. You don't know which bit of the code it generated perfectly (amazing!) or which bits it just hand waved and didn't actually do anything. It is frustrating that it didn't do a 100% job but it is still a miracle it got 95% of it.</p>]]></description>
      </item>
      <item>
        <title>Twitter</title>
        <link>https://liquidx.net/posts/2026/twitter-delete</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2026/twitter-delete</guid>
        <pubDate>Thu, 07 May 2026 00:00:00 GMT</pubDate>
        <description><![CDATA[<p>Finally deleted all my tweets from Twitter.</p>
<p>The web API rate limits to 200 deletes per 15 mins, and I had 13209 tweets - roughly 16 hours if nothing went wrong (and it did a few times.)</p>
<p>My Twitter For You feed immediately went from geeky japanese cartographers to AI bros arguing with each other.</p>]]></description>
      </item>
      <item>
        <title>Island</title>
        <link>https://liquidx.net/posts/2026/island</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2026/island</guid>
        <pubDate>Sat, 02 May 2026 00:00:00 GMT</pubDate>
        <description><![CDATA[<p>Have any of you wondered why island is not pronounced is-land?</p>]]></description>
      </item>
      <item>
        <title>Google</title>
        <link>https://liquidx.net/posts/2026/google</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2026/google</guid>
        <pubDate>Wed, 08 Apr 2026 00:00:00 GMT</pubDate>
        <description><![CDATA[<p><img src="https://content.liquidx.net/media/posts/2026-04-08-google-ef97205b.jpg" alt="google_badge.jpg"></p>
<p>After 6863 days, yesterday was my final day at Google. Two months shy of 19 years.</p>
<p>Grateful for all the opportunities. The opportunity to travel the world, to work on the first iPhone app, to bring people together, to work with wonderful people, to redesign Google, to work on my favourite products and to eat all the free food.</p>
<p>There's no next thing, I'm retiring to spend time completing my side quests.</p>]]></description>
      </item>
      <item>
        <title>Exploring Terminal Nav on Web</title>
        <link>https://liquidx.net/posts/2026/terminal-nav-web</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2026/terminal-nav-web</guid>
        <pubDate>Thu, 12 Feb 2026 00:00:00 GMT</pubDate>
        <description><![CDATA[<p>I thought it would be fun experiment to build a terminal inside my own website and use it as a navigation UI. And this site is now the result of this after a few hours of trial and error.</p>
<p><img src="https://content.liquidx.net/media/posts/2026-02-12-terminal-nav-web-128f8930.gif" alt="liquidx-net-2026.gif"></p>
<p>The main principle here is that it should be functional as a website and also (quasi-)functional as a command line but primarily for operating this website. This is based on an earlier <a href="https://aversion.one/of/terminal">Terminal prototype</a> I vibe-coded a few months ago, but then subsequently extracted, and more or less rewrote by hand to fix a bunch of layout behaviors which was really hard to get Claude Code or Gemini/Antigravity or Codex to get right.</p>
<p>The code knows the structure of the website, and converts that into a virtual filesystem that exists in JS. The main commands that were necessary was <code>cd</code> and <code>ls</code>. Each page is a directory (no files), and if a <code>cd</code> command was detected, is does a proper page navigation using SvelteKit's <code>goto()</code>. As a bonus, the output of <code>ls</code> is slightly marked up and instrumented so clicking on the links will emulate typing the command in and navigating.</p>]]></description>
      </item>
      <item>
        <title>Snowline 2026</title>
        <link>https://liquidx.net/posts/2026/snowline-2026</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2026/snowline-2026</guid>
        <pubDate>Sun, 08 Feb 2026 00:00:00 GMT</pubDate>
        <description><![CDATA[<p><a href="https://snowline.jp/">Snowline</a> is a passion project of mine that has been going on for the past three years. The idea was to create a well-designed, data-driven site about every ski resort in Japan.</p>
<p><img src="https://content.liquidx.net/media/posts/2026-02-08-snowline-2026-d0a45440.png" alt="snowline-2026-screenshot.png"></p>
<p>After moving to Japan and discovering there were resorts apart from <a href="https://snowline.jp/en/location/niseko-united">Niseko</a> and <a href="https://snowline.jp/en/group/hakuba">Hakuba</a>,  I wanted to know more about these slopes. I wanted to go to check them out. Not long later, I stumbled across a magazine that claimed to list all 430 resorts in Japan. I bought that magazine and proceeded to manually write down in a spreadsheet every ski resort there was.</p>
<p><img src="https://content.liquidx.net/media/posts/2026-02-08-snowline-2026-e1a91d74.png" alt="snowline-sheet.png"></p>
<p>I wanted to know more. I wanted to know how big it was, how popular it was, how much snow it was getting. So I began to collect this all in the spreadsheet, it grew and grew. Soon it outgrew the spreadsheet and migrated into a database.</p>
<p>The more data was in there, the more data I wanted. It expanded to course maps, it expanded to GPS traces, it expanded to even webcam images. Today, it is a 4GB database with 1.2TB of images/data and code base of 800+ files.</p>
<p>What about that magazine? Every year a new edition of this magazine was released and every year the number of total resorts would go down. This year, the magazine reported that there are now 391 ski resorts remaining in Japan. Snowline does count a few more because of the way we classify what constitutes a resort.</p>
<p><img src="https://content.liquidx.net/media/posts/2026-02-08-snowline-2026-ffd35f88.jpg" alt="nippon-gelande-2020-2026.jpg"></p>
<p>I'm building this because I want to use it. If only one person, myself, found it useful, it would of been worth it. The act of building it helped me understand these ski resorts more, find these small ones only a few people know about and plan these weeks long road trips to hit these resorts to fulfil <strong>my goal of riding all the resorts in Japan.</strong></p>]]></description>
      </item>
      <item>
        <title>Github Packages 403</title>
        <link>https://liquidx.net/posts/2025/github-packages-403</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2025/github-packages-403</guid>
        <pubDate>Mon, 15 Dec 2025 00:00:00 GMT</pubDate>
        <description><![CDATA[<p>Today I learned that Github Packages is not free for private packages. And I learnt it by getting a cryptic <em>npm</em> 403 error when accessing <code>https://npm.pkg.github.com</code> from <em>npm</em>.</p>
<p>After about 1GB of transfers for Github Packages, it will "run out of quota" and you'll get a 403. I thought it was an authentication issue, so I racked my brains on why the Github Access Token was not working. But an authentication error will give you a 401.</p>]]></description>
      </item>
      <item>
        <title>Day 6 Akakura Onsen</title>
        <link>https://liquidx.net/posts/2023/day-6-akakura-onsen</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2023/day-6-akakura-onsen</guid>
        <pubDate>Fri, 10 Mar 2023 00:00:00 GMT</pubDate>
        <description><![CDATA[<p>This was a day of highs and lows.</p>
<p>Staying in Akakura, we had to at least try a day in Akakura, even though it’s not such an interesting mountain. Moreover, there must be a discount our hotel can offer us.</p>
<p>It was complicated.</p>
<p><img src="https://content.liquidx.net/media/posts/2023-03-10-day-6-akakura-onsen-7d79bfd3.webp" alt="cbd8db27-622f-47e5-845a-678ce4a2f1c2_1020x920.webp"></p>
<p>Akakura has two resorts, the Kanko, and the Onsen. If you only do the onsen side, you can get a ticket just for that. And that’s what most of the hotels sell you, discounted tickets for JUST the Onsen side. The Akakura Kanko, Akakan if you will, is owned by the luxurious hotel up at mid mountain. Legend has it that the previous Japanese Emperor stays there for vacations. So no one has a discount except for guest at their fancy hotel. You can get a combined ticket for ¥6000, which isn’t too much, but why pay 100% more for just a few more lifts?</p>
<p>This particular day wasn’t spectacular, and our main event was to see my friend’s kids learn how to snowboard, so really we needed just to stay on the part of their resort they were in. Akakura Onsen it is then.</p>
<hr>
<p>That’s where it started to go down hill. First, on paper, this place has 12 lifts! Twelve is a lot for a Japanese resort. Even Akakan only has 5. However, on the day we were on the mountain, only half the lifts were operating! Times are tough, so let’s give them a pass.</p>
<p><img src="https://content.liquidx.net/media/posts/2023-03-10-day-6-akakura-onsen-919a449b.webp" alt="9d6fbdbb-70eb-47ed-83bb-65cd5ba21c5c_1631x1405.webp"></p>
<p>We started on the left side and slowly made our way across to the right. Once we got to the right, we got another punch to the gut. The high speed quad on the right hand side broke down. Our only way to get back to the other side of the mountain. So no way back, apart from the bus.</p>
<hr>
<p>It was a packed powder day, there were some untouched patches here and there where we duly ruined. If you look at the map, it’s mostly green runs. And definitely can confirm, it’s a beginner’s mountain.</p>
<p><img src="https://content.liquidx.net/media/posts/2023-03-10-day-6-akakura-onsen-65a52462.webp" alt="1e2c8069-57b2-4950-93a3-dfd6271293d3_2994x2851.webp"></p>]]></description>
      </item>
      <item>
        <title>Day 5 Suginohara</title>
        <link>https://liquidx.net/posts/2023/day-5-suginohara</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2023/day-5-suginohara</guid>
        <pubDate>Thu, 09 Mar 2023 00:00:00 GMT</pubDate>
        <description><![CDATA[<p>We got more snow in the afternoon after we left, so we though that Sugi would have another powder day. We were still high on the previous day. The weather cleared and we thought, finally some visibility with powder.</p>
<p>The top lifts were open unlike the previous day we were there. We thought the strategy would be to go where others hadn't gone yet. Every year I go to Sugi, every year I regret going to the top runs. They're much steeper, more bumpy than the runs below. Sure enough, every time I go, every time I get disappointed. </p>
<p>In the end we went back down to the Panorama runs and did the trees and the wide open mellow fields of powder. Still lots of powder lines to be had on a blue bird day.</p>
<p><img src="https://content.liquidx.net/media/posts/2023-03-09-day-5-suginohara-1bbd3220.webp" alt="847e3635-d03f-49b2-9d90-feeb1c160fe8_4080x3072.webp"></p>
<hr>
<p>The other reason I like Suginohara is because not all the restaurants on piste are operated by the resort itself. At the bottom of the high speed lifts is a set of three four restaurants that are independent, my favorite being the <a href="https://goo.gl/maps/NTarAU78bZjup5CS8">St Anton</a>. Along with delicious Japanese pork cutlet and the usual, it features a caramel popcorn machine!</p>
<p><img src="https://content.liquidx.net/media/posts/2023-03-09-day-5-suginohara-9b86d3cf.webp" alt="96f5a12e-7857-465b-a203-7d9d6bddb9a3_4080x3072.webp"></p>]]></description>
      </item>
      <item>
        <title>Day 4 Suginohara</title>
        <link>https://liquidx.net/posts/2023/day-4-suginohara</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2023/day-4-suginohara</guid>
        <pubDate>Wed, 08 Mar 2023 00:00:00 GMT</pubDate>
        <description><![CDATA[<p>It was January, and a proper Japow day. We're hitting 30cm of fresh powder. Sugi is my go to resort if it's a big powder day. My rule about powder days is longest, fastest, widest. The best place to go is where there are the (1) longest runs, (2) fastest lift and (3) widest area. Longest runs is that you want the runs to be long so you ride the powder as long as possible, fastest lifts so you can fit more runs in and widest area so it doesn't get tracked out so quickly.</p>
<p><img src="https://content.liquidx.net/media/posts/2023-03-08-day-4-suginohara-2870174a.webp" alt="56d993fb-e0d0-4a68-b219-19b65b1cacaf_1242x1616.webp"></p>
<p>Sugi hits all three in my book. The Panorama and Dynamic courses both end up on the High Speed Quad. Panorama is a super wide run, you can see the run from miles away. It's so wide that it's what I used to recognize as Sugi when I'm spotting ski resorts around Nagano.</p>
<p><img src="https://content.liquidx.net/media/posts/2023-03-08-day-4-suginohara-a710bc4e.jpg" alt="50b8a1b3-e28b-418c-9d4c-931481a6dc54_2890x1469.jpg"></p>
<p>It was snowing all day, and every run there were fresh lines to be had, the groomed run was being buried under the snow. So much snow was coming down during the day that we broke for lunch and came back out to nearly fresh lines, even better conditions then at the start of the day. Even though the top lifts were not open, it didn't matter at all.</p>
<p><img src="https://content.liquidx.net/media/posts/2023-03-08-day-4-suginohara-2ee8522f.jpg" alt="be0262d4-da8a-4c0b-8953-f1e044f81d85_3502x1914.jpg"></p>]]></description>
      </item>
      <item>
        <title>Day 3 Kagura</title>
        <link>https://liquidx.net/posts/2023/day-3-kagura</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2023/day-3-kagura</guid>
        <pubDate>Wed, 01 Mar 2023 00:00:00 GMT</pubDate>
        <description><![CDATA[<p>Recounting my third day at Kagura. There wasn't any new snow, but some lifts and runs that were closed the days before were open so we went searching for some untouched powder!</p>
<p><img src="https://content.liquidx.net/media/posts/2023-03-01-day-3-kagura-5b46ab80.jpg" alt="a7270072-0cf5-44bb-a7a0-5faedafeefb2_3414x1998.jpg"></p>
<p>To be honest, it was a pretty ordinary day, we had a pretty relaxed go around the place, left a little early and was treated to a Hong Kong style Egg Waffle at the based of Mitsumata!</p>
<p><img src="https://content.liquidx.net/media/posts/2023-03-01-day-3-kagura-84fd0d1a.jpg" alt="3ada5d80-e329-4a36-b3b0-1c957a8249b9_3072x4080.jpg"></p>]]></description>
      </item>
      <item>
        <title>Day 2 Kagura</title>
        <link>https://liquidx.net/posts/2023/day-2-kagura</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2023/day-2-kagura</guid>
        <pubDate>Tue, 28 Feb 2023 00:00:00 GMT</pubDate>
        <description><![CDATA[<p><img src="https://content.liquidx.net/media/posts/2023-02-28-day-2-kagura-cb44e81e.webp" alt="716a5e8d-e283-4528-bba5-bd5c75addb70_960x1280.webp"></p>
<p>It's a sort-of-powder-day! Hey, not bad for December. It was snowing quite a bit on the mountain the day before, so I was getting excited about getting out early to the lifts. </p>
<p>All my training getting up early at 5am for early morning meetings this year is now paying dividends. Getting to first lifts is easy for me. I went out as early as I could, though I didn't manage to get the first ropeway up. A lot of people showed up ahead of me on the way to the ropeway. The mountain ended up not getting too much new snow overnight though.</p>
<p><img src="https://content.liquidx.net/media/posts/2023-02-28-day-2-kagura-c0ddf502.jpg" alt="c54becda-506c-4dce-9044-766ac0007127_1392x980.jpg"></p>
<p>A few areas were still closed off, including what I considered the best area in Kagura, the Kagura Lift 5 that only opens for half a day, on a good day. On windy days they don't even bother. Neither for early season like today. This area is where you can drop through the trees into a huge open bowl that lands you back somewhere within the resort. Alas, it is not running.</p>
<hr>
<p>Instead, I used the opportunity to try to ride the whole mountain. Even though I've been to <a href="https://snowline.jp/en/area/kagura">Kagura</a> a few times, I never managed to ride all the way across from end-to-end. After what seemed like a never ending series of lifts, I managed to get all the way to the far end of Tashiro. At this end there is a dramatically named Dragondola, a gondola that connects Kagura to Naeba. I was disappointed the gondolas were not shaped like a dragon.</p>
<hr>
<p>On the Tashiro side, there were a few powder stashes and we rode around as a large group through the trees, destroying any smooth untouched powder we could find. I'd rate that a powder-ish day.</p>
<p>One of these are my lines:</p>
<p><img src="https://content.liquidx.net/media/posts/2023-02-28-day-2-kagura-670f2a64.webp" alt="c500b15d-0fcf-45d6-9694-5ab5aa7c4075_960x1280.webp"></p>]]></description>
      </item>
      <item>
        <title>Day 1 Kagura</title>
        <link>https://liquidx.net/posts/2023/day-1-kagura</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2023/day-1-kagura</guid>
        <pubDate>Wed, 22 Feb 2023 00:00:00 GMT</pubDate>
        <description><![CDATA[<p><img src="https://content.liquidx.net/media/posts/2023-02-22-day-1-kagura-76d1699a.jpg" alt="b1af1bed-0325-4053-90c2-d050a040fd0c_3024x4032.jpg"></p>
<p>A warm up trip before the main part of the season. This is the last week of December, usually considered a little too early. Conditions down in Honshu are usually a bit iffy at this time of the year. Going to <a href="https://snowline.jp/en/area/kagura">Kagura</a> is usually a good bet. Kagura is quite high up in elevation, so if there is any snow, Kagura will get it.</p>
<p>Yuzawa Kogen, Niigata, where Kagura is located, was not getting much snow at the start of the season. Many of the resorts in the area had snow cover but were delaying opening this week because it wasn’t enough.</p>
<hr>
<p>For this trip, I'm driving from Tokyo to Kagura. Driving to the snow from Tokyo requires getting a car with snow tyres. As it rarely snows in Tokyo, rental cars from Tokyo don't usually have snow tyres. Finding those gems in the haystack, where the haystack is the unusable mess that are rental car company websites, can be a time consuming chore. In the last three years, I've been using this one branch of a popular car rental company here called <a href="https://www.2525r.com/">NicoNico</a> which surprisingly always has cars that come with snow tyres. They actually call these tires studless tyres, but that's too much of a rabbit hole to go down. </p>
<p>The drive itself is about 4 hours, probably an hour less if I lived in northern Tokyo rather than the south. It's a pleasant drive, on the expressway for all but the last 20 minutes. Excitement built as we started seeing snow on the road.</p>
<hr>
<p>We got there around 10am, so not even close to first lifts, but it looked like the conditions were pretty good there. I've been to Kagura on 3 separate trips, but never this early in the season. Surprisingly, there's some fresh powder lines to be had on our run.</p>
<p><img src="https://content.liquidx.net/media/posts/2023-02-22-day-1-kagura-46b87e1b.jpg" alt="d97556f5-6d31-4d7e-8356-63e9607f941e_1179x1179.jpg"></p>
<p>Bonus was it started snowing during the day, so already I was getting excited about tomorrow. I had to remind myself that today is really getting to know my snowboard again after a season apart.</p>]]></description>
      </item>
      <item>
        <title>Hello Again, Snow</title>
        <link>https://liquidx.net/posts/2023/hello-again-snow</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2023/hello-again-snow</guid>
        <pubDate>Tue, 21 Feb 2023 00:00:00 GMT</pubDate>
        <description><![CDATA[<p>I’m back at the snow this year, and back writing about my meanderings in the white stuff. We’re already past half way into the season and I realized I hadn’t shared what has been an wonderful season. First time in three years that I’m not snowboarding alone!</p>
<p><img src="https://content.liquidx.net/media/posts/2023-02-21-hello-again-snow-720dc7c8.jpg" alt="3d2cf93b-a9f7-4e73-a5f0-fbd8da89f712_1920x1080.jpg"></p>
<p>I spent the last two years exploring ski resorts by myself from Nagano to Iwate. Managing to get 56 different resorts (out of nearly 400 in Japan). This year was the first time I got to share some of my gems with friends, ride some amazing powder and experience surprises from off-the-grid resorts to the super-popular.</p>
<p>Starting tomorrow, I’ll be recapping the days I’ve been at the snow, powder days or not. 🏂</p>]]></description>
      </item>
      <item>
        <title>Lessons from creating a Generative Adversarial Network Model</title>
        <link>https://liquidx.net/posts/2022/gan-model</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2022/gan-model</guid>
        <pubDate>Sun, 14 Aug 2022 00:00:00 GMT</pubDate>
        <description><![CDATA[<p>Continuing my adventures in learning about Machine Learning. I spent most of this week learning how to write and train my own [[GAN]], took a few detours into looking at MLOps.</p>
<h1>Background</h1>
<p>I realized recently that the proliferation in ML techniques, tools and models presents a huge change in the tech industry. We're probably several years out from when ML is ingrained in software development, much like 15 years ago with the launch of the iPhone, mobile application development.</p>
<p>As a software developer (engineer), I wanted to understand this space, not just through using Midjourney, Dall-E or whatever new popular ML model  outputs, but understand how to implement them, what are the easy and difficult parts of them and use that information to understand the potential of this space.</p>
<h1>The Project</h1>
<p>I wanted to do something that was a mix of some of my past experiences together with exploring the space using ML. I decided to try use the same image generation techniques that are present in image generation services but using it on a smaller domain of images, mobile screenshots.</p>
<p>The idea is to create my first trained Generative Adverserial Network to generate plausible looking screenshots of mobile apps.</p>
<h1>Process &#x26; Tools</h1>
<p>The data I'm using is the Rico dataset which consists of a corpus of around 60,000 Android screenshots from around 2017. This is not very recent, but its a large enough database for my first attempt.</p>
<p>The ML library I'm using is Keras &#x26; Tensorflow, mostly because that is the one I'm most familiar with. For the environment, I'm using Google Colab. Google Colab is a great choice because it's relatively inexpensive on the Pro plan to get access to a GPU for training purposes.</p>
<p>I've also started exploring some different ML ops tools that are starting to popup, including Hugging Face for access to a wide range of user-contributed datasets, Neptune and Weights and Biases for training tracking.</p>
<h1>Learnings</h1>
<p>For any type of problem in ML, there are numerous tutorials online. GANs are no different. There are so many different ways to do it, I followed the three most promising ones and combined parts that I liked.</p>
<p>I decided to begin at a small size and work my way up, looking at generating a 64x32 image, which is roughly double the size of the MNIST datasets that most tutorials use as their example data set (28x28).</p>
<p>The first attempt I made at this, I essentially trained a noise generator that just would not produce anything that resembled a screenshot at all. I really couldn't understand what I was doing wrong. ML models are hard to debug on their own, but with GANs being a combination of two different models, it's even harder to figure out what was wrong.</p>
<p><img src="https://content.liquidx.net/media/posts/2022-08-14-gan-model-0ec0cbf4.png" alt="download (3).png"></p>
<p>The problem here was there isn't much variety, and more training just seemed to cycle through different colors and splotches in the generated images.</p>
<p>In order to debug this, I went back through every line of the code, from how the model is constructed to the training look and looked to understand what everything was doing. When following tutorials, its tempting to just copy and modify parts you want. This turns out to be great if the dataset you have is the same. Because I'm using a totally different dataset, all the training hyperparamters needed to be different. Probably the most important is the training rate. Turns out, my training rate was 2 orders of magnitude lower than it should be, hence none of the models were getting far at all.</p>
<p>The models themselves were also buggy, lots of the layers in model had various scalar numbers that weren't self explanatory, it was until I read more tutorials where the authors explained more why certain numbers were chosen that I realized what they were for.</p>
<p>The state of the art for training GANs have also evolved since they were first introduced, people had discovered what they made them not converge or stall, and introducing improvements to it.  But it was hard to tell whether those improvements were made with the tutorials I was following.</p>
<p>In the end, I ended up taking quite a lot of inspiration from the tutorials but tweaking them.</p>
<p>As I was using Google Colab, the runtime itself would occassionally disconnect (inactivity or time limits.) Even with my paid plan, this would still happen.  So I had to turn how to do training checkpointing, saving model outputs to a file and restoring back the state.</p>
<p>Finally, I wanted to be able to monitor the state of the training outside of Google Colab, so I encountered ML tools like Neptune and Weights and Biases which provide a  simple python library that allowed me to send progress metrics to a server which I could monitor during the training process.</p>
<h1>Training and Faliures</h1>
<p>As I had suspected, it is not easy at all to train a GAN. I did get it to start generating some decent low-res screenshots for a few attempts. Though, the quality never got to a point where I was really happy with.</p>
<p>It seemed like tweaking the learning rates is important as you are trying to keep the discriminator and generator error rates to be close to one another. Yet, I couldn't get mine to converge no matter how long I trained them. My takeway was that after about 50 epochs, you can generally know whether the model was working by seeing if there's any downward trend in the generator model.</p>
<p>When I trained this for longer, it would inevitably get worse! This was not what I was expecting, it seemed that the longer I trained it, the more the generator model would output less variety. For instance, generating screenshots for Android, I would expect quite a lot of Material Design looking apps with different colored bars, FABs, etc. But the longer I trained the model, the more white/grey looking it would become.</p>
<p>When I trained it to produce higher-res images, like 128x64 (4x the size), the model would experience Mode Collapse, which is that it would stop  generating variety of different images to fool the discriminator, but it would zero in on only several variations. This meant that the results looked very similar to one another.</p>
<p><img src="https://content.liquidx.net/media/posts/2022-08-14-gan-model-e57c2c33.png" alt="download (8).png"></p>
<h1>Next Steps</h1>
<p>I'm going to continue to learn how to create a properly working GAN for my small domain set. The things I'm going to try is to learn more about data processing of the data set to balance it more.</p>
<p>Maybe will work on training it with a much smaller but cleaner dataset to see whether I end up with something better.</p>
<p>This will mean some time to explore image labelling and improve the image loading code that I have with my demo.</p>
<p>The goal is to get the model to the point that it can generate some good enough plausible screenshots that I'd share a demo of it.</p>]]></description>
      </item>
      <item>
        <title>Tohoku Tour, Finally</title>
        <link>https://liquidx.net/posts/2022/tohoku-tour-finally</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2022/tohoku-tour-finally</guid>
        <pubDate>Sat, 23 Jul 2022 00:00:00 GMT</pubDate>
        <description><![CDATA[<p>I’m writing this from Tohoku, those snowy and cold prefectures at the northern tip of Honshu (main island). It’s been a dream of mine to travel around here and experience the mountains of Tohoku.
<img src="https://content.liquidx.net/media/posts/2022-07-23-tohoku-tour-finally-e89cdb8b.png" alt="tohoku-map.png"></p>
<p>This is my first time snowboarding in Tohoku, it is something I’ve been trying to do for the past two years but have failed. First attempt was in 2020, I planned out a two week ski trip out in Iwate with friends. But because of the terrible snow season that year, we made a last minute change of plans and went up to Hokkaido (Kiroro, Furano and Asahidake). The following year, in 2021, I had plans to do a few weeks in Zao Onsen and further north, but got injured in Togari Onsen, Nagano before I could realize my plans.</p>
<p>The resorts here are surprisingly less well known than their Nagano and Hokkaido counterparts. In terms of travel time, these places are just as quick to get to from Tokyo, even quicker than Hakuba or Hokkaido. Because these places are less well known to foreigners, they don’t have the style of accommodation like serviced apartments or AirBnbs.</p>
<p>It’s snowy here. The mountains though generally lower than ones in Nagano, but not as low as Hokkaido, yet still receives plenty of snow. <a href="https://edition.cnn.com/travel/article/aomori-japan-snowiest-cities/index.html">Aomori City is one of the snowiest cities</a> in Japan, <a href="https://snowm.app/area/geto-kogen/">Geto Kogen</a> in Iwate boasts the highest snow depths in Japan, while <a href="https://snowm.app/area/zao-onsen/">Zao Onsen</a> in Yamagata is famous for their field of snow blasted trees in February resulting in some amazing scenery. Not to mention, <a href="https://snowm.app/area/hakkoda/">Hakkoda Ropeway</a> in Aomori, a backcountry mecca.</p>
<p><img src="https://content.liquidx.net/media/posts/2022-07-23-tohoku-tour-finally-ca218286.jpg" alt="zao-onsen-snow.jpg"></p>]]></description>
      </item>
      <item>
        <title>Rendering Mountains 9</title>
        <link>https://liquidx.net/posts/2022/rendering-mountains-9</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2022/rendering-mountains-9</guid>
        <pubDate>Tue, 31 May 2022 00:00:00 GMT</pubDate>
        <description><![CDATA[<p>Now that the light is attached the mountain, when rendering the grid of mountains, it should be lit nicely. Oops, but my scene now crashes after rendering the 10th mountain. Turns out, there is a limit to the number of lights you can have. I think it’s because each light is using a texture for the shadow calculation and there is a maximum number of textures that can be used.</p>
<p>So this is a bit of a problem, maybe what I can do is isolate the light and move it around instead and capture each light separately? I’m not quite sure what to do.</p>]]></description>
      </item>
      <item>
        <title>Rendering Mountains 8</title>
        <link>https://liquidx.net/posts/2022/rendering-mountains-8</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2022/rendering-mountains-8</guid>
        <pubDate>Mon, 30 May 2022 00:00:00 GMT</pubDate>
        <description><![CDATA[<p>I wanted to attach the light to the mountain and place it relative to the mountain rather than the whole scene. So I refactored the code to attach a new THREE Group to the mountain scene and add the light on to it. This way, when I draw the big matrix of mountains, each mountain is lit properly.</p>
<p>As I’m modifying the lights, I realized that I am using a directional light, point light, hemisphere light and the ambient light. So that’s a lot of lights. The hemisphere light is not really needed here, I think it was there just for my experimentation. The ambient light needs to be there for global illumination.</p>
<p>I don’t like the DirectionalLight though because it appears from infinity and goes to infinity so I can’t change the cut-off or frustrum so it only lights a certain area. Point light is nice but it also cannot limit to a certain area unless you change the decay.</p>
<p>Rather, I’ve replaced those lights with a Spotlight which I can change the frustrum and it lights it in a circular area. Changed the light to be placed at the top of the mountain at some fixed height. Likely need to move to relative to the height of the area.</p>]]></description>
      </item>
      <item>
        <title>Circles instead of Rectangles</title>
        <link>https://liquidx.net/posts/2022/circles-instead-of-rectangles</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2022/circles-instead-of-rectangles</guid>
        <pubDate>Tue, 17 May 2022 00:00:00 GMT</pubDate>
        <description><![CDATA[<p>I want to try making the rectangular mountain areas to be circular. So I’ve started doing some work to calculate the geometry. My initial problem to solve is to work out the arc equation. First off, trying to find where the circular area will intersect with the edge of the map tiles so that I can figure out how they clip. I suspect it will be a full week of maths for me.</p>
<p><img src="https://content.liquidx.net/media/posts/2022-05-17-circles-instead-of-rectangles-e2a8feba.png" alt="circle_arc.png"></p>
<p><img src="https://content.liquidx.net/media/posts/2022-05-17-circles-instead-of-rectangles-73809299.png" alt="packing_layout_code.png"></p>
<p>A weird thing happened in the last few days where I couldn’t push my debugging console that is on Firebase. The few times I tried to push, my internet would stop working. It turns out firebase hosting doesn’t like it when I tried to deploy something like 1000 PNG files. So instead, I moved all the textures and maps on to cloudstorage instead of deploy with the firebase hosting.</p>]]></description>
      </item>
      <item>
        <title>Packing Layout</title>
        <link>https://liquidx.net/posts/2022/packing-layout</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2022/packing-layout</guid>
        <pubDate>Sun, 15 May 2022 00:00:00 GMT</pubDate>
        <description><![CDATA[<p>I managed to write a pretty rudimentry box packing algorithm that laid boxes out from the top and pushing boxes up into the gaps where it would fit. The algorithm works ok but a little bit of randomness is needed to minimize the gap. Maybe I can run a regression over it.</p>
<p>The result is in <a href="https://observablehq.com/@liquidx/japan-resort-geometries">Observable as a prototype</a> which I’ll move the implementation into my codebase to run for the 3D map.</p>
<p><img src="https://content.liquidx.net/media/posts/2022-05-15-packing-layout-dd12f722.png" alt="packing_layout.png"></p>]]></description>
      </item>
      <item>
        <title>Mountain Poster</title>
        <link>https://liquidx.net/posts/2022/mountain-poster</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2022/mountain-poster</guid>
        <pubDate>Sat, 14 May 2022 00:00:00 GMT</pubDate>
        <description><![CDATA[<p>Fixed some fallout from my refactoring from a few days where I made the mountain rendering code work across a few different projects. Using Vue has been great to be able to share the same general renderer across different views. However, there is the part where I want to render all the mountains in a single WebGL instance which means I can’t share the Vue component, but have to share the internals.</p>
<p>I had been looking at <a href="https://github.com/pmndrs/react-three-fiber">react-three-fiber</a> and also trios (Vue equivalent) and I really like the declarative way to construct a scene and possibly allow for reuse. I am tempted to rewrite all my rendering code to take advantage of that model, but I’m not sure if I’ll regret it. Maybe I’ll leave that for later.</p>
<p>I fixed the minor issues I had after my refactoring to get my multi-mountain renderer working again — this time, instead of rendering just a few mountains, its rendering 300 mountains which makes it really slow. The whole render took 11 minutes to do, without any of the trees and lift lines! This is a proof of concept render to see if I could render that many mountains in the scene. I had to make a few adjustments like expanding the clipping plane.</p>
<p><img src="https://content.liquidx.net/media/posts/2022-05-14-mountain-poster-702789b0.png" alt="mountain_poster.png"></p>
<p>A few things are missing still, obviously the models that sit on top of the mountains, and also the texture maps I want to put on the top. I’m expecting that adding those will increase the render time more. I want to add back the shadows by adding a light per mountain, or maybe just faking the shadows using ambient occulsion. I’d like to figure out a way to draw a perspective label on each mountain.</p>
<p>The final piece which I’m using Observable to do, is to develop an algorithm to place each mountain in the scene. Right now the mountains are all in a grid which already looks nice, but I want to make it a little more packed, more space around the big mountain like Shiga Kogen and less space between the tiny mountains.</p>]]></description>
      </item>
      <item>
        <title>Resorts on a Map</title>
        <link>https://liquidx.net/posts/2022/resorts-on-a-map</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2022/resorts-on-a-map</guid>
        <pubDate>Mon, 09 May 2022 00:00:00 GMT</pubDate>
        <description><![CDATA[<p>Some nights when I can’t get to sleep, I think about hard problems until I fall asleep. Last night I thought about what would be the way to arrange the mountains in a way that would look aesthetically pleasing but also algorithmically so it also isn’t manual.</p>
<p>There’s enough manual things I’m doing already with collecting the data that I want the rest of it to be algorithmic as much as possible.</p>
<p>I went and tried to play around with <a href="https://observablehq.com/">Observable</a>. I’m a heavy user of d3, and think Observable is probably the best designed data-vis platform around, built by the creator of d3. I used it to prototype my <a href="https://observablehq.com/@liquidx/rotating-japan">initial rendering of the Japan outline</a> so here I’m going to try use it to play with different layout algorithms.</p>
<p>The first thing though was just to get the data to load, so I built up a way to just draw the rectangles that represent the size of the resort. I draw out all the rectangles manually on my “data input days”, so it’s nice to see them all in one map, like a ski resort constellation.</p>
<p><img src="https://content.liquidx.net/media/posts/2022-05-09-resorts-on-a-map-de26f6a6.png" alt="mountain_map.png"></p>
<p>It’s such a thing of beauty to see it all working. However, I encountered a weird bug and resulting knowledge.</p>
<p>All the bounding boxes for the ski resorts are drawn by hand myself. I have been steadily using an open source GeoJSON editor to allow me to draw shapes on a map that I then export into GeoJSON, stored in a database (Firestore). For this visualization, I exported all that data into a single GeoJSON file and loaded it in. Once loaded, I used d3-geo to map the lat lons to screen coordinates, output an SVG path and the label of the resort area on the map.</p>
<p>However, when I first did this, there were a few issues, one was that some areas would render as huge world size rectangles and second, some of the areas, the centroid could not be found using the regular <code>path.centroid()</code> methods. I was stumped as to why.</p>
<p>After a good night’s sleep, I imagined that maybe the Polygon vertices had directionality (aka winding rule). Since I had seen that you can define a hole that could be cut out of a polygon, I imagined that the polygons follow some sort of winding rule. And indeed, that was the reason, Since I had haphazardly draw these bounding box manually, sometimes I would draw the bounding box clockwise, sometimes anti-clockwise, and so all the anti-clockwise ones ended up being negative-space polygons causing d3-geo to get confused by them.</p>
<p>I used <a href="http://turfjs.org/docs/#feature">turfjs</a> to convert the polygon into a LineString, detect whether it the clockwise-ness <code>booleanClockwise</code> and then reverse the order of the points if it was. That fixed the problem for me. And so now I have a beautiful collection of bounding boxes.</p>]]></description>
      </item>
      <item>
        <title>Refactoring Rendering</title>
        <link>https://liquidx.net/posts/2022/refactoring</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2022/refactoring</guid>
        <pubDate>Sun, 08 May 2022 00:00:00 GMT</pubDate>
        <description><![CDATA[<p>Refactoring day.</p>
<p>A few things I wanted to work on, getting the terrain map showing up on (Snow Map) <a href="http://snowm.app/">snowm.app</a>, fixing the lighting, fixing the normals and attempt again at rendering a large map.</p>
<p>First I had to refactor the custom Three JS implementation into something I could share between two projects. The way the project is set up is there’s a monolithic repo containing all my development data tools (<code>snow-data</code>) and another repo that contains the Snow Map site (<code>snow-map</code>). Previously I wanted to share code between the two sites but not been able to. I wasn’t really sure how to set this up. Both projects are now using ESM across all the code (<code>import * from x</code>) rather than the old Node JS CommonJS style (<code>require()</code>).</p>
<p>I’ll try to split the sharable things into a separate repo. The less repos the better, but I’ve still ended up with four:</p>
<ol>
<li>Base repo containing all parsing, calculation and data manipulation (snow-base)</li>
<li>Mountain renderer containing all the Three JS code to render the scene. (snow-mt)</li>
<li>Vue component for the Mountain render. (snow-mt-vue)</li>
</ol>
<p>I’m not confident this is the right set up, having less repos would be better in my mind, but I wanted to separate out some Vue dependencies so that if I needed to run snow-mt code outside of Vue, I could still do that.</p>
<p>To install local dependencies using npm, I use:</p>
<p><code>npm install ../snow-mt</code></p>
<p>This results in symlinks in <code>node_modules/</code>, and this seems to work fine but I can feel that VSCode is starting to get confused, especially when I search the code base.. I’m not sure how others handle sharing and development. It is more simple when there is a single mono repo. Nice thing is that I can edit everything now in one editor but if there are changes that span across the dependencies and the web UI itself, I need to make multiple commits.</p>
<p>I tried again to render the large multi-mountain map. It is getting very slow when I try to render the single map with just 20 mountains. I’ve run into a data issue already because the same mountain gets rendered multiple times (Niseko) due to there being multiple resorts on that. I need to go back and clean up the data so that when the resort is nearby/connected, we render the single mountain rather than multiple.</p>
<p>Finally, I attempted to fix the lighting on my renders. I thought I could shortcut this by playing with this in Blender rather than in code. My idea was to bring the models into Blender by exporting them in GLTF with Three JS. However, turns out the way I’m using vertex shaders to deform the plane can’t really be exported using GLTFExporter. At one point I had custom vertex mesh generator that happened outside of the vertex shader, but that just created too many vertices and ground Three JS to a halt. I might have to bring that back again.</p>]]></description>
      </item>
      <item>
        <title>Data Day</title>
        <link>https://liquidx.net/posts/2022/data-day</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2022/data-day</guid>
        <pubDate>Sat, 07 May 2022 00:00:00 GMT</pubDate>
        <description><![CDATA[<p>Data day.</p>
<p>I spent most of the day manually inputting data into my system to get all the dimension information of all the know ski resorts that are still open.</p>
<p>Along the way, made some improvements to my tools in preparation for trying to render hundreds of ski resorts at once. I’m not quite sure yet how to do it, putting so many models on to the screen might cause an issue, but we’ll try and see.</p>
<p>I will want to do one thing which is fix the normal calculation first. After watching some of the game development videos from <a href="https://www.youtube.com/c/SebastianLague">Sebastian Lague</a>, I’m thinking I want to do some work to stylise the rendering a bit too to make it more cartoony and have some more dramatic lighting.</p>]]></description>
      </item>
      <item>
        <title>Cloud Storage</title>
        <link>https://liquidx.net/posts/2022/cloud-storage</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2022/cloud-storage</guid>
        <pubDate>Fri, 06 May 2022 00:00:00 GMT</pubDate>
        <description><![CDATA[<p>Tidy up day.</p>
<p>I spent some time renaming some files in my Google Cloud Storage bucket because it was becoming a mess.</p>
<p>There are a few automated things that happen in the cloud storage bucket, so I had to rename all the paths and then modify all the code that was refering to those paths. When I did the rename, I first tried to use <code>bucket.getFiles()</code> and then rename all the files that matched my prefix. However, it would kill my network router because I didn’t check how many files were coming back. My computer was making 1000s of simultaneous requests. It took me a few gos to figure out why, once I did, switched to number to 20 per page and everything worked fine.</p>]]></description>
      </item>
      <item>
        <title>Rendering Mountains 7</title>
        <link>https://liquidx.net/posts/2022/rendering-mountains-7</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2022/rendering-mountains-7</guid>
        <pubDate>Thu, 05 May 2022 00:00:00 GMT</pubDate>
        <description><![CDATA[<p>I worked a bit more on making the tree placement better. I’m rendering out a texture map that denotes where the lift lines and ski runs are and using it like an image mask to avoid placing trees where the pixel (UV) is lit. In the process I discovered a few weird things.</p>
<p>First, I was using a 512x512 texture for a 512x512 plane in 3D space. I thought that would be good, but I had a lot of trouble with the low resolution of the texture. After a lot of head scratching and double checking my code, I decided export 1024x1024 textures for a 512x512 plane. Magically, that fixed the resolution issues I had. I still don’t know why that helped as the improvement in resolution was more than just 4x.</p>
<p>Secondly, I haven’t wrapped my head around UV Mapping textures. UV Mapping is mapping a 2D image on to a 3D surface. Even when I’m mapping it on to a plane, I initially couldn’t quite make it work like I had in my mind. As seen below, the surface I have is not a full 512x512 square, it’s clipped to only draw out a defined area. Thus, the texture mapping needs to be modified so it doesn’t map the 512x512 texture to a now smaller size, thus making it wrong. When it is wrong, the below white area is where the lift line should be, but it’s squeezed because the entire texture is being used even though the area is only a proportion.</p>
<p><img src="https://content.liquidx.net/media/posts/2022-05-05-rendering-mountains-7-9bc83570.jpeg" alt="abashiri_1.jpeg"></p>
<p>I chose the most simple mountain to debug the maths to get the UV mapping right using the Texture transforms, but couldn’t get the texture to map properly until I hooked up a <a href="https://cocopon.github.io/tweakpane/">tweaker</a> to the texture mapping and realized the Y axis was flipped!</p>
<p><img src="https://content.liquidx.net/media/posts/2022-05-05-rendering-mountains-7-8e44c89c.png" alt="abashiri_2.png"></p>
<p>Lastly, on the tree placement, the ski runs are actually quite narrow in reality compared to what you see in resort ski maps. They’re probably exaggerated for visibility, so when you take the real proportional dimensions it doesn’t look that clear there is a ski-run. I modified the texture to have a light green shading for the off-piste areas so the white ski runs are easier to see I reckon that I need to increase the density of the trees and give the ski runs a bit more artificial width. I have some ideas on how to maybe do this when generating the textures — possibly jittering the ski run position a tiny bit when I put the texture map together.</p>
<p><img src="https://content.liquidx.net/media/posts/2022-05-05-rendering-mountains-7-54cab658.png" alt="zao_onsen_4.png"></p>
<p>The result is pretty nice though, but the green makes it feel like summer rather than winter.</p>
<p>A few things I want to improve on now. Up till now, I’ve been using flat shading for the mountain. I really like the effect of flat shading because it exaggerates the lighting to give you a much more clearer picture of the angle of the slope compared to smooth shading. The reason also is that I haven’t figure out how to correctly generate my normal (vector) maps yet.</p>
<p>Now that I have this, I need to go back to generate more data for more mountains. The goal is to put all of Japan’s ski resorts into a single render. I’m slowly getting there.</p>]]></description>
      </item>
      <item>
        <title>Rendering Mountains 6</title>
        <link>https://liquidx.net/posts/2022/rendering-mountains-6</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2022/rendering-mountains-6</guid>
        <pubDate>Wed, 04 May 2022 00:00:00 GMT</pubDate>
        <description><![CDATA[<p>Yesterday I thought I’d use a particle system to draw the trees. But now that I read up on it, it’s really only for rendering a texture/material and not really for drawing a model like a tree. However, I’ve come across <a href="https://threejs.org/docs/#api/en/objects/InstancedMesh">InstancedMesh</a> which will duplicate a model efficiently as long as the model has the same material geometry, and only apply a transform to it. That seems like exactly what I wanted.</p>
<p><img src="https://content.liquidx.net/media/posts/2022-05-04-rendering-mountains-6-0f88ba6d.png" alt="zao_onsen_2.png"></p>
<p>The way I’ve randomly placed the trees, they’re covering the lift lines (and eventually when I have the runs mapped out). So, I’m now doing a brute force point distance to line calculation for every tree that I’m plotting. This is hugely inefficient - takes 20s to calculate all the points. The next step is to draw those areas I don’t want trees into a texture map and probably just do a hit test on the tree location. For now it looks quite good with the trees:</p>
<p><img src="https://content.liquidx.net/media/posts/2022-05-04-rendering-mountains-6-38d707e4.png" alt="zao_onsen_3.png"></p>]]></description>
      </item>
      <item>
        <title>Rendering Mountains 5</title>
        <link>https://liquidx.net/posts/2022/rendering-mountains-5</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2022/rendering-mountains-5</guid>
        <pubDate>Mon, 02 May 2022 00:00:00 GMT</pubDate>
        <description><![CDATA[<p>I didn’t like the cone shaped trees I had, so I drew some in Blender and exported out as GLTF. GLTF is a common model exchange format that’s supported out of the box in Three JS. I don’t really know much about the other formats. My first attempt at loading the GTLF model didn’t seem to load the material. But when I inspected it more closely it seemed to load it with metallic at 100% causing it to render trees black. To make the trees visible, I had to make them larger than realistic. But if I make them more realistic sized and draw lots of trees, the scene grinds to a halt. I think I need to use a particle system instead to draw the trees.</p>
<p><img src="https://content.liquidx.net/media/posts/2022-05-02-rendering-mountains-5-b9078466.jpeg" alt="sugadaira_trees.jpeg">
Blender is so powerful that I wish I had some of their shader UI and material selection in Three JS, but maybe if I can get the materials to load properly in Three JS using the GLTFLoader, I’d get the best of both worlds.</p>
<p>I learn most of Blender through YouTube videos and a lot of the stuff I learn is super relevant to this whole rendering exercise.</p>]]></description>
      </item>
      <item>
        <title>Sugadaira, Nagano 菅平高原スキー場</title>
        <link>https://liquidx.net/posts/2022/sugadaira-nagano</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2022/sugadaira-nagano</guid>
        <pubDate>Mon, 02 May 2022 00:00:00 GMT</pubDate>
        <description><![CDATA[<p>#Published</p>
<p>It is pretty much the end of the snow season, but I can still reminisce about some ski resorts I’ve been to this year. This post will talk about <a href="https://snowm.app/area/sugadaira/">Sugadaira Kogen Snow Resort</a> 菅平高原スキー場.</p>
<p>Sugadaira itself is marketed as a single snow resort entity, but within it are three areas, Pine Beak, Taro and Davos. Taro and Davos are connected via a short tunnel under a road (or you can also walk across the road). These two areas combined and is also marketed as <a href="https://snowm.app/area/sugadaira-hare/">Sugadaira Hare</a>, though the website hasn’t been updated since 2007.</p>
<p><img src="https://content.liquidx.net/media/posts/2022-05-02-sugadaira-nagano-9a1ffdae.png" alt="Pasted image 20230318163039.png"></p>
<p><a href="https://pinebeak.jp/">Sugadaira Pine Beak</a> itself is a 5 min drive from the other two resorts. You can’t walk or ski over to it. Pine Beak is split into two parts though there is a traverse between them.. Nearby, around the same distance is actually another resort called <a href="https://snowm.app/area/rewild-ninja/">Rewild Ninja</a>, but they’re not as part of the same group.</p>
<p><img src="https://content.liquidx.net/media/posts/2022-05-02-sugadaira-nagano-618dc7c3.png" alt="Pasted image 20230318163053.png">
On the Sugadaira Ski Map they’re fictionally the same mountain, but in reality, it’s an amalgamation of three or four areas.</p>
<p>When it comes to representing these resorts, there is no consensus on how to represent them. Some sites will represent them as a single resort, some will list them out separately. I think the areas Pine Beak is separate enough that you can consider that as a separate resort, the Taro and Davos area, though connected, needs a traverse and walk and I think that’s separate enough to call them separate areas.</p>
<h2>Tickets</h2>
<p>The ticket you buy from the Taro/Davos (aka. Hare) side technically gives you access to the whole Sugadaira. But if you go over to the Pine Beak area, you need to swap your ticket at the ticket office for a Pine Beak ticket. The whole Sugadaira use RFID tickets, but for some reason they need this extra step.</p>
<p>The day I rode Sugadaira, I started off on the Taro area, rode over to Davos and then decided to try Pine Beak. Once I got there, I got really confused why I couldn’t use my ticket even though I was told it should work. After a few minutes of trying, I gave up. Only afterwards did I read the instructions on the website and it said that I needed to swap the ticket at the ticket office when switching areas.</p>
<h2>CAT Skiing</h2>
<p>At the top of Davos, there’s a little hut next two CATs. It’s one of the most accessible CAT skiing places I’ve been to. You can pay an extra ¥4000 for the ticket to go on the CAT. The day I went, there was very low visibility, so I decided to not try it. You can see the course it’ll take you on the map, and I’d give it a go next time on a good day.</p>
<p><img src="https://content.liquidx.net/media/posts/2022-05-02-sugadaira-nagano-6f81beab.png" alt="sugadaira-cat.png"><img src="https://content.liquidx.net/media/posts/2022-05-02-sugadaira-nagano-26556075.jpg" alt="70626c85-686b-4c32-bc1f-0b578e795a10_1000x748.jpg"></p>]]></description>
      </item>
      <item>
        <title>Rendering Mountains 4</title>
        <link>https://liquidx.net/posts/2022/rendering-mountains-4</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2022/rendering-mountains-4</guid>
        <pubDate>Sun, 01 May 2022 00:00:00 GMT</pubDate>
        <description><![CDATA[<p>I started to draw lift lines and trees on to the mountain, so it looks more interesting and informative. My first pass is pretty amateurish, just drawing a line and some struts for the lift lines and then some very fake random trees on.</p>
<p><img src="https://content.liquidx.net/media/posts/2022-05-01-rendering-mountains-4-0e02ea28.jpeg" alt="zao_onsen_1.jpeg">
The line is drawn using Three JS’s <a href="https://threejs.org/docs/#api/en/geometries/ExtrudeGeometry">ExtrudeGeometry</a> since regular lines don’t have any thickness in WebGL. More accurately, I’m using TubeGeometry, which as the name suggests, makes a tube. The trees are drawn using cones on a grid, and the randomized a little within a tight grid.</p>
<p>I’m still trying to figure out shadows in the background, as you can see, it’s kind of a mess now that I’ve added trees. A friend suggested that I look at Ambient Occlusion. I seriously didn’t know what that was. After watching a few YouTube videos on how to do this in Blender, I figured out that it’s fake lighting/reflection on the edges of surfaces to make them look like real shadows. There are a few examples of <a href="https://threejs.org/examples/webgl_postprocessing_ssao.html">SS Ambient Occlusion</a>, but the examples all look pretty intense. Maybe I’ll go fix the shadows at the end. At least the side walls should have some ambient occlusion.</p>]]></description>
      </item>
      <item>
        <title>Rendering Mountains 3</title>
        <link>https://liquidx.net/posts/2022/rendering-mountains-3</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2022/rendering-mountains-3</guid>
        <pubDate>Sat, 30 Apr 2022 00:00:00 GMT</pubDate>
        <description><![CDATA[<p>Back to rendering mountains today.</p>
<p>I started debugging shadows in Three JS. I know that shadows are complicated in 3D, as it needs to figure out which parts are obscuring the light. There are a few default shadows that can be turned on in Three JS but none of them really look that good.</p>
<p><img src="https://content.liquidx.net/media/posts/2022-04-30-rendering-mountains-3-beaabacb.jpeg" alt="mountain_cake.jpeg"></p>
<p>This is the best I could get using their highest fidelity setting and manual tuning, but the shadows look like a series of rectangles stacked together. I may need to get deeper into shaders to create my own nice shadows. For now, this will do.</p>]]></description>
      </item>
      <item>
        <title>Identifying Mountains 3</title>
        <link>https://liquidx.net/posts/2022/identifying-mountains-3</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2022/identifying-mountains-3</guid>
        <pubDate>Fri, 29 Apr 2022 00:00:00 GMT</pubDate>
        <description><![CDATA[<p>In <a href="https://snowm.app/">Snow Map</a> there are some resorts with missing elevation and area info. I am manually generating this now by looking at the mountain itself and measuring it.</p>
<p>I did a few of them manually but then realised I should probably automate this and replace all the numbers with real surveyed numbers (rather than relying on the resort to report it.) Resorts tend to exaggerate. A few resorts quote their area size as the largest, but when you look at where you can ride, it’s only a small proportion of it, because either they don’t operate the lifts or out-of-bounds for them.</p>
<p>It’ll be hard to automate this, as it’s not a simple lookup of the elevation of the mountain.</p>]]></description>
      </item>
      <item>
        <title>Cylinders instead of Boxes</title>
        <link>https://liquidx.net/posts/2022/cylinders-instead-of-boxes</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2022/cylinders-instead-of-boxes</guid>
        <pubDate>Thu, 28 Apr 2022 00:00:00 GMT</pubDate>
        <description><![CDATA[<p><img src="https://content.liquidx.net/media/posts/2022-04-28-cylinders-instead-of-boxes-d3a41ff2.png" alt="mountain_quad.png"></p>
<p>The rectangular tiles look a little weird because I rotate the tile so that the angle of the slope is always 135º — but the rectangular box itself is clipped along the latitude longitude lines. This means the angle of the edges of the box don’t line up because each mountain is at a different angle.</p>
<p>One way to mitigate this is to use a circular base (make it a cylinder). Though, it seems much more complicated to do the maths.</p>
<p><img src="https://content.liquidx.net/media/posts/2022-04-28-cylinders-instead-of-boxes-87d555ef.png" alt="mountain_bug_1.png"></p>
<p>The way the rectangular box works is, each tile is 512x512 and if the mountain area is more than 512x512, multiple tiles are laid together, with the excess chopped off and sidewalls rendered. When it is a box, it’s easy to cut a straight edge with simple arithmetic. But with a circle it gets more complicated - but doable.</p>
<p>The other thing I’ll need to do, is be able to pass that arc into the vertex shader for the sidewalls. The sidewalls’ shaders deform the top edge by looking at the height map of the texture to make it match the terrain. If the shape is now an arc, it will require passing in that arc shape into the vertex shader, which I’m not sure how to do properly. Maybe there’s a mathematical equation for this?</p>
<p><img src="https://content.liquidx.net/media/posts/2022-04-28-cylinders-instead-of-boxes-f5b2fe15.png" alt="circle_mountain.png"></p>
<p>What I cannot not do is merge all the terrain into a single image texture. While this will make it super simple to do the maths, the size of the texture is restricted (2048 on mobile?), so if my texture is detailed enough or the area is large enough, it will exceed it and then I’d have to go do some tiling anyway.</p>
<p>So, looks like I’ll need to do a bunch of maths here.</p>]]></description>
      </item>
      <item>
        <title>Rendering Mountains 2</title>
        <link>https://liquidx.net/posts/2022/rendering-mountains-2</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2022/rendering-mountains-2</guid>
        <pubDate>Wed, 27 Apr 2022 00:00:00 GMT</pubDate>
        <description><![CDATA[<p>Fixing some bugs with the renderer discovered when trying to render Hakkoda (by request).</p>
<p><img src="https://content.liquidx.net/media/posts/2022-04-27-rendering-mountains-2-2131d28d.png" alt="mountains_normal.png"></p>
<p><img src="https://content.liquidx.net/media/posts/2022-04-27-rendering-mountains-2-f93c2bc8.jpeg" alt="hakkoda.jpeg"></p>
<p>I use <a href="https://lodash.com/">lodash</a> a lot to do simple three line things that I can’t be bothered writing. However, this time it bit me. I tried to use <code>merge()</code> thinking that it would merge two dictionaries together and concat the values if the keys were the same. It did something weird. So instead, I wrote the three lines that I should of written at the start.</p>
<p>I don’t know what lessons it taught me, but it taught me something.</p>]]></description>
      </item>
      <item>
        <title>Rendering Mountains At Night</title>
        <link>https://liquidx.net/posts/2022/rendering-mountains-at-night</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2022/rendering-mountains-at-night</guid>
        <pubDate>Wed, 27 Apr 2022 00:00:00 GMT</pubDate>
        <description><![CDATA[<p>Last week, I posted a photo of a set of 40 mountains rendered in 3D. What is this?</p>
<p><a href="https://twitter.com/liquidx/status/1517754356317429760?s=20&#x26;t=wuEq2Ldlj1ywTiKTa8HPnw">Twitter post</a>
<img src="https://content.liquidx.net/media/posts/2022-04-27-rendering-mountains-at-night-832f082e.png" alt="mountain_twitter.png"></p>
<p>I'm exploring visualizing geography without maps to represent the relative size, gradient and shape of different snow resorts in Japan. This view I posted is a very early experimental view to verify that what I'm doing is working.</p>
<p>Every evening, I'm spending an hour or so on this problem, often solving a data issue, a rendering issue or making a tiny improvement. Every night I learn something new about the elevation data, maths, tiling and even the tools that I'm using like Vue JS, Three JS and more. I have a fun idea end product I'm building towards which will be revealed once I get there.</p>
<p>The mountain data is manually drawn by myself using some tools I built. The rough boundary, top and bottom positions are added by manually inspecting collected data and satellite imagery.</p>
<p>The elevation is coming from publicly available elevation data in DEM format which is sliced in to tiles at different zoom levels split into a "standard" tiling system called slippy tiles. This tile system splits the world into a regular grid system at different zoom levels.</p>
<p>The rendering is done in WebGL through ThreeJS, through the help of custom shaders that directly use a DEM as a texture and do something similar to what a displacement map does, except with much higher resolution.</p>
<p>The model is built entirely algorithmically, orientating the mountain so that the main slope is always facing the same perspective. The scale for every mountain is the same (with one exception), so you can get a sense of how big each mountain is to another.</p>
<p>There is still a lot to go on this project. Having got to this stage, there are a few things I want to improve on. First off, the rectangular cut outs are not all rotated at the same angle making it look messy. Maybe I should use a circular cut out (which will be more maths for me to work out). Second, the way the elevation displacement works has caused some of the mountains to go "off-screen" because they're higher. Third, the larger areas like Kagura and Shiga Kogen cannot be contained within the viewport so I could tweak the camera angle more.</p>
<p>Many issues to still resolve, but I'm happy with the milestone.</p>
<p><img src="https://content.liquidx.net/media/posts/2022-04-27-rendering-mountains-at-night-85f674fb.png" alt="mountains_dark.png"></p>]]></description>
      </item>
      <item>
        <title>Identifying Mountains 2</title>
        <link>https://liquidx.net/posts/2022/identifying-mountains-2</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2022/identifying-mountains-2</guid>
        <pubDate>Tue, 26 Apr 2022 00:00:00 GMT</pubDate>
        <description><![CDATA[<p>A light day for me. I made sure my data tools worked with the new “virtual” resorts (e.g. Madarao-Tangram) that I made. Sure enough, there were some bugs with my tools that assumed I always have predicted sizes for each mountain. These new mountains broke that assumption so I needed to both fix the code and also fix the data.</p>
<p>As explained previously, I made up some resorts that are not real, but logical resorts. Those resorts exist because (a) the resorts are really close together, and (b) you can ride between them. So while they might not be owned by the same company, they’re logically connected. There are several of these that I’ve been to, like Akakura Onsen and Akakura Kanko, or Madarao and Tangram.</p>
<p>Creating these virtual resorts help with eventually drawing them out on a map and categorizing GPS traces because if they’re connected, you’re surely going to get someone who will traverse into them</p>]]></description>
      </item>
      <item>
        <title>Identifying Mountains 1</title>
        <link>https://liquidx.net/posts/2022/identifying-mountains-1</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2022/identifying-mountains-1</guid>
        <pubDate>Mon, 25 Apr 2022 00:00:00 GMT</pubDate>
        <description><![CDATA[<p>For SnowMap, I have to assign my own unique identifier to each of the mountains. Then I have to map that identifier to some data sources I am crawling. This makes it hard if I ever have to change the identifier. Also, some mountains are very complicated, like Sugadaira, Niseko and Shiga Kogen.</p>
<p>Sugadaira itself is known as a ski resort, but within it is 3 areas, Pine Beak, Taro, Davos. Taro and Davos are connected via a short tunnel under a road (or you can also walk across the road), and is also marketed as Sugadaira Hare. Pine Beak itself is a 5 min drive from the other two resorts, but it’ll appear on the same map. So, how are you supposed to represent this or count it? There doesn’t seem to be a consensus on other sites that aggregate Japanese ski resorts.</p>
<p>Shiga Kogen is even more complicated. Itself contains 18 different logical ski resorts, and it’s split between four areas — Yakebitaiyama Okushiga, Central, Yokote and Kumanoyu. Though, Yakebitaiyama, Okushiga and Central are connected together so you can ski across the whole area. Yokote and Kumanoyu are 10 mins drive away from them, they’re loosely connected, I think there’s a traverse between them, but it’s also a 5 min walk. So do you count Shiga Kogen as one resort or 4 resorts or 18 resorts?</p>
<p>Niseko be contrast is more simple, four distinct resorts, Hanazono, Grand Hirafu, Niseko Village, Annupuri. When you hear from people saying that they’re going to Niseko, I usually assume they’re going to Grand Hirafu (the most popular and largest part), but you could be staying at the foot of any of those, and you can buy a ticket to just one area.</p>
<p>One way to slice these is whether you can buy a ticket for an area. For Shiga Kogen, you can buy an all-mountain ticket, or a lift ticket for just one part of the resort. If that’s the determination, then Shiga Kogen could be split into 5 areas, Okushiga, Yakebitaiyama, Central (includes 12 resorts), Yokote and Kumanoyu.  I’m not sure whether that is the best way to split these</p>
<hr>
<p>Back to what I was working on. The way the resorts are named on the Snow Map are not entirely consistent. Some sources of data treat multiple slopes as one resort, some treat them separately. So I created some logical resorts like “Niseko United” which encompasses all four of the Niseko mountain resorts. Skipping some of the details, this means I needed to rename the identifiers of the mountains and all the subsequently generated data.</p>
<p>I have a lot of crawled data indexed by identifier names that’s on Google Cloud Storage, so I needed to rename them with the new identifiers. My first attempt at using <code>bucket.getFiles</code> didn’t end well because there were too many files in my GCS, leading to out of memory in Node JS. So instead, I wrote a simple progressive renamer that used pagination to rename the files</p>]]></description>
      </item>
      <item>
        <title>Rendering Mountains 1</title>
        <link>https://liquidx.net/posts/2022/rendering-mountains-1</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2022/rendering-mountains-1</guid>
        <pubDate>Sun, 24 Apr 2022 00:00:00 GMT</pubDate>
        <description><![CDATA[<p>I’m thinking about how do I go from rendering rectangular tiles for the mountain to a circular (cylinder). And it seems to much more complicated the more I think about it. First off, because each mountain is a difrerent size, I chose to not resize the tiles (512x512) and instead arrange them into a bed of 3D tiles.</p>
<p>The maths for the rectangular tiles is fairly easy, but if I use a cylinder, this strategy will get complicated.</p>
<p>First off, I would need to slice the cylinder into several pieces, that means I can’t use the CylinderGeometry directly, I’d need to figure out the geometry to create a cylinder segment. I can probably do this by some maths around circle and calculate the vertices along the edge of the circle, then slice them up using some rectangular geometry.</p>
<p>The technique I used to modify the edge vertices using the vertex shader would have to map the linear edge of the cylinder to points on the circumference of the circle. That is do-able with some more maths.</p>]]></description>
      </item>
      <item>
        <title>Tazawako, Akita たざわ湖スキー場</title>
        <link>https://liquidx.net/posts/2022/tazawako</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2022/tazawako</guid>
        <pubDate>Sat, 12 Mar 2022 00:00:00 GMT</pubDate>
        <description><![CDATA[<p><em>This is one of my first posts about a resort I’ve visited. I’m still working on what would be interesting to write about.</em>
<img src="https://content.liquidx.net/media/posts/2022-03-12-tazawako-cf410dfd.jpg" alt="ea6e92c4-d275-40ce-9c28-850e810297be_640x430.jpg">
<a href="https://snowm.app/area/tazawako/">Tazawako</a> is such a wacko name. It translates to Lake Tazawa, but I’m glad they called it Tazawako. This lake is in Akita Prefecture. I’ve never been to Akita Prefecture, and even having come here, I don’t feel.I can justifiably tell people I’ve been to Akita.</p>
<p>Tazawako reminds me of Lake Tahoe, not only they sound similar, but on the mountain, you get a beautiful view of the lake. Towards the afternoon, the lake shimmers with the low golden hour light. Just beautiful.
<img src="https://content.liquidx.net/media/posts/2022-03-12-tazawako-57a0916f.jpg" alt="5c15170b-f4e0-4cd9-816d-9c700e5e0340_4032x3024.jpg"></p>
<p>I hadn’t heard of this place before, but it has shown up in my <a href="https://snowm.app/">Japan Snow Map</a> data as something that was close to where I was staying in Shizukuishi, a drive under an hour east. It wasn’t one that was on my hit list for Tohoku, but seeing it was reachable, I couldn’t resist making that easy drive.</p>
<p>The resort was a decent size by Japan standards, likely the biggest in Akita. Six lifts cover the mountain, though one was out of action (a common theme that I’ll talk about in the future). Six is on the top-end of resorts in Japan.</p>
<p>Scattered around though are evidence of the age, this lovely hand painted ski maps were placed at the top of one of the lifts.</p>
<p><img src="https://content.liquidx.net/media/posts/2022-03-12-tazawako-ebf28ce4.jpg" alt="tazawako-handdrawn.jpg"></p>
<p>The day I went wasn’t a powder day, but a good time going around the mountain, the runs connect well and lots of options to explore. It seemed like the day I went, the only food option was the food court at the bottom. It was huge, just like their food portions. One order could easily feed three people. The highlight though was a shoes-off cafe that served up a nice coffee and views to work from.</p>
<p><img src="https://content.liquidx.net/media/posts/2022-03-12-tazawako-f07bcf36.jpg" alt="tazawako-cafe.jpg"></p>
<p>I’m surprised I’ve never heard of this place, it ought to be more well known, with the Shinkansen station close by. If you’re even in the mood to venture out to Akita, <a href="https://snowm.app/area/tazawako/">Tazawako</a> is one of my recs.</p>]]></description>
      </item>
      <item>
        <title>out of 492</title>
        <link>https://liquidx.net/posts/2022/out-of-492</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2022/out-of-492</guid>
        <pubDate>Sat, 05 Mar 2022 00:00:00 GMT</pubDate>
        <description><![CDATA[<p>I’ve now been able to snowboarded in 51 out of the 492* ski resorts in Japan, that’s just over 10% of all resorts! Since moving to Japan in 2018, it’s been a project of mine to try experience as many different resorts as possible.</p>
<p>This season isn’t over yet, but up until now I visited 27 new ski resorts this season. That’s more than all the resorts I’ve been to in previous years put together. (Is that how you express that??) It is a little misleading. What constitutes as a resort seems to be quite blurry.
<img src="https://content.liquidx.net/media/posts/2022-03-05-out-of-492-940b0238.jpg" alt="niseko-map.jpg"></p>
<p>Let’s take Niseko as an example. Many people who had visited before would know that Niseko is actually four resorts in one, Grand Hirafu, Hanazono, Annupuri and Niseko Village. So I count those as four separate resorts. Or you could count that as a connected resort as one, which would just be “Niseko”. Or you could count the ones that might have their own tickets (eg. Grand Hirafu and Hanazono have a single ticket).</p>
<p><img src="https://content.liquidx.net/media/posts/2022-03-05-out-of-492-3bab55af.gif" alt="shiga-kogen-map.gif"></p>
<p>The extreme version of this is Shiga Kogen. Shiga Kogen is technically 18 different resorts. But if one applies the connected area rule, then it is technically three different resorts, Yokote-Shibutoge, Kumanoyu and the rest. Shiga Kogen is a lot more complicated in terms of how you slice it. Each of those 18 resorts are run separately, but is marketed as a single resort — even if physically you need to drive 15 mins from the west end to the east end. Ultimately, I decided to count them separately because on the ground, they are quite distinct, many of them have a pretty clear traverse which you must take in order to get from one to another. That is enough for me to call them a resort even if it has only the one lift or one run.</p>
<p>In the next few posts, I’ll hopefully introduce a few of them from my experience.</p>
<p>*Note: Previously I said there were 500, but I was counting ones that are closed this season, the real accurate number is now 492.</p>]]></description>
      </item>
      <item>
        <title>Getting to the snow by train</title>
        <link>https://liquidx.net/posts/2022/getting-to-the-snow-by-train</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2022/getting-to-the-snow-by-train</guid>
        <pubDate>Fri, 11 Feb 2022 00:00:00 GMT</pubDate>
        <description><![CDATA[<p><img src="https://content.liquidx.net/media/posts/2022-02-11-getting-to-the-snow-by-train-e24192b0.jpg" alt="jr-ski-ski.jpg"></p>
<p>For decades, Japan Rail (JR) have been running a campaign called <a href="https://plusalphadigital.com/jr-skiski-campaign/">JR SKI SKI</a>. The whole premise is to get you to go to the snow via Shinkansen. From Tokyo station, there’s a 1.5 hour train that can get you to Echigo Yuzawa, one of the many ski resort hotspots. One most convenient one being <a href="https://snowm.app/area/gala">Gala Yuzawa</a> which has their own dedicated station that only opens in the winter months, with a gondola  inside the train station!</p>
<p>The first time I tried to go to the snow via Shinkansen, I was nearly the only person who was carrying my board bag. Shinkansen don’t have that much room for bags, so it’s actually a hassle. So little room that in 2020, JR started asking you to reserve space and charge you extra for bringing big bags into the train. If you’re bringing your bag, you are obviously doing it wrong.</p>
<p>The Japanese way is to send your bags to the ski resort or hotels through <a href="https://www.kuronekoyamato.co.jp/ytc/en/send/services/ski/">Takyubin (宅急便)</a> by Yamato Delivery (the logo is a black cat carrying a cat.) You can do it from your home, your local 7-11 or even from the airport. You only need to send it 2 days before to guarantee it will be there at the destination waiting for you (it’ll likely get their overnight, but they like to under promise). Pick them up when you arrive at the ski resort or hotel. If you get them directly from the resort, they’ll have a changing area for you to use too. Once you’re done, you just send it back home the same way you did. All of this only costs you about 2000 yen (USD $20) one-way.</p>
<p>So if you ever come with your boards (or just big bags), and planning to take a train from the airport, don’t bring your bags with you on the train, send the bags from the <a href="https://www.jalabc.com/en.html">JAL ABC</a> or Yamato counters.</p>]]></description>
      </item>
      <item>
        <title>Japanese Snow Resorts</title>
        <link>https://liquidx.net/posts/2022/japanese-snow-resorts</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2022/japanese-snow-resorts</guid>
        <pubDate>Thu, 03 Feb 2022 00:00:00 GMT</pubDate>
        <description><![CDATA[<p>Did you know that Japan has more than <a href="https://snowm.app/">500 snow resorts</a>? Niseko, Hakuba, Nozawa Onsen might be the most popular ones those outside of Japan would recognize and visited. You can tell by the amount of Airbnbs that are available in those areas, or the holiday apartments for rent, or the amount of English.</p>
<p>In the less-known places, those places that aren’t known outside of Japan, the scene is very different. There are no Airbnbs, there are no holiday apartments. Some may have very little on mountain accommodation, don’t even think about a toilet or bathroom in your room. Some of these might only have a single lift, or just a ropeway and you find your own way down. Some of these might barely have any parking. Some of these are run by a grandpa or grandma. Some of these might have dirt cheap tickets. Some of them owned and operated by the government. Some of them in places you didn’t even think would have resorts.</p>
<p>Exploring these places, experiencing these resorts and riding the beautiful snow in Japan is what I have decided to do.
<img src="https://content.liquidx.net/media/posts/2022-02-03-japanese-snow-resorts-0e748a8a.png" alt="japan-map.png"></p>]]></description>
      </item>
      <item>
        <title>About Riding Japan's Snow</title>
        <link>https://liquidx.net/posts/2022/about-riding-japans-snow</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2022/about-riding-japans-snow</guid>
        <pubDate>Tue, 01 Feb 2022 00:00:00 GMT</pubDate>
        <description><![CDATA[<p>This is a newsletter (or blog, or journal) about snowboarding in Japan, from me, Alastair.</p>
<p>After moving to Japan, I learned a lot about going snowboarding in Japan, and getting more and more curious about where and how to go snowboarding in Japan.</p>
<p>A bit about my profile and level: I'm a snowboarder who enjoys going fast on-piste, riding white lines in deep powder (not so deep), and the occasional tree run in the side-country. I don’t hike or do tricks in the park. I’m not experienced in back-country at all. I’m learning to improve my riding, learn a few tricks and jumps along the way.</p>
<p>When away from the snow, I'm obsessed with mapping days on the snow, learning about Japanese snow culture and beautiful boards.</p>
<p>Follow my adventures, successes and failures at figuring snowboarding in Japan.</p>]]></description>
      </item>
      <item>
        <title>GO WST</title>
        <link>https://liquidx.net/posts/2021/go-wst</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2021/go-wst</guid>
        <pubDate>Sat, 13 Nov 2021 00:00:00 GMT</pubDate>
        <description><![CDATA[<p>GO WST</p>
<p>Walk See Talk</p>
<p>An idea of an app that is a bit inspired by Pikmin Bloom, but reduce the gamification element of walking, instead make it about enjoying the walk and exploring. Not just number of steps, but number of stops and pace.</p>
<p>Make it so that you never have to really worry about the app until after the day is over, where you try to piece together your day and write words about it.</p>]]></description>
      </item>
      <item>
        <title>Learning</title>
        <link>https://liquidx.net/posts/2021/learning</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2021/learning</guid>
        <pubDate>Tue, 13 Jul 2021 00:00:00 GMT</pubDate>
        <description><![CDATA[<p>During these pseudo-lockdown days of COVID, I have a lot more time
on my hands to scratch itches, build those things that I thought
I would enjoy building, learning how wrong some of my assumptions
are, and how occassionaly, something beautiful comes out of it.</p>
<p>Ever since March 2020, I've learnt to build a few new categories
of app that I thought were out of reach. Things that the start
of lockdown I only imagined, but now are real things.</p>
<ul>
<li>I've learnt how to build fairly complex Vue apps.</li>
<li>I've learnt how to build React Native apps, how good the tool chain is, but also how much they suck at the last mile.</li>
<li>I've learnt how to build Swift UI (and how close they are conceptually to React, Vue.)</li>
<li>I've learnt how to use TensorFlow and Keras to train my own recognizers from scratch.</li>
<li>I've learnt how to use TensorFlow JS and TensorFlow models to build web-browser face detectors.</li>
<li>I've learnt how to use Google Cloud AI to build WhereCam.</li>
<li>I've learnt how to use Google Cloud Storage (and not to use it for high throughput writing.)</li>
<li>I've learnt how to build authenticated server-less web and iOS apps using Firebase Auth.</li>
<li>I've learnt how to how to automate a lot of things using Google Cloud Functions and Cloud Scheduler.</li>
<li>I've learnt how to build complex Webpack projects (and moved my whole website on to it that deploys on a Github push.)</li>
<li>I've learnt how to programmatically use Google Spreadsheets to drive a few projects (<a href="https://covid19japan.com/">covid19japan</a>, <a href="https://snowm.app/">snowm.app</a>).</li>
<li>I've learnt how to use Tesseract to do PDF parsing for <a href="https://covid19japan.com/">COVID tracking</a>.</li>
<li>I've learnt how to use d3 pretty effectively to <a href="https://snowm.app/">make maps</a>.</li>
<li>I've learnt how to use Three.js to render some cool mountains.</li>
<li>I've learnt how to use Firestore, Firebase Auth and Firebase Storage in all sorts of cool ways.</li>
<li>I've learnt how to build and publish an npm.</li>
</ul>
<p>I've learnt a lot in the last 18 months, and when we do finally come out of lockdown, I'm going to miss this.</p>]]></description>
      </item>
      <item>
        <title>Personal Projects 2021</title>
        <link>https://liquidx.net/posts/2021/personal-projects-2021</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2021/personal-projects-2021</guid>
        <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
        <description><![CDATA[<h1>Personal Project Journal 2021 July</h1>
<p>July has been a month where I put a lot of my spare time working on various personal projects of mine, and one that managed to make a lot of progress on several projects of mine.</p>
<h2>Data House</h2>
<p>Data house is something that I've dreamed in my head for a while, so it was good to be able to make it into a reality. I didn't really talk about it apart from several screenshots I've posted on Twitter and Instagram.</p>
<p>I started collecting data about myself about a decade ago when I saw Feltron do these amazing data visualizations. I felt that I needed to collect some data, and turns out, I collected a lot of data, both manually and automatically. I think this is the most unified I've tried to tie all my data bases together.</p>
<p>I'm very happy with how it has turned out, I need to go back and spend more time curating it, but being able to create a Trips page, a full hour by hour breakdown of what I was doing and some mini visualizations of the data in that hour.</p>
<p>This will be an ongoing project of mine.</p>
<p>I'm especially proud of the fact that I managed to get all the data to be hosted in the cloud, under my control. The raw data itself is checked in to github for redundancy and tracability.</p>
<h2>Mem</h2>
<p>Mem is a bit of an itch scratcher. Mem is my idealized version of what I want a note taking system to be. It's probably closest to what Notion and Evernote is, but it is a lot more simple. It's like the very basic version of Evernote, Notion, Pinterest, Delicious and Instapaper combined. It's my little pack rat project that allows me to store anything into there from as many entrypoints as possible. I can send a few words, a few paragraphs, a URL and even a photo to it. And it just stores it in a basic structured data format that will allow me to query and visualize it in the future.</p>
<p>I was able to build this from scratch within a week. Still a lot of things not yet working, for instance, I discovered that Instagram serves up a log in page if you hit a instagram post URL from a Google server, but if you try from a non-datacenter server, everything is returned fine. No combination of headers has yet gotten me around this, but there must be a way to.</p>
<p>This can evolve in all sorts of different ways, it could become my manual collection endpoint, I could be sending me mood, random events and notes in to there like I do with Foursquare, if Foursquare ever shuts down. I'm pretty excited to build more on this.</p>
<h2>Snow Map</h2>
<p>I went back to doing more with Snow Map. Snow map has a lot of different aspects to it, the website snowm.app, the manually curated data set, semi automated crawled data that I'm manually aggregating into a master index of all the snow resorts in Japan.</p>
<p>The flaw so far with this is a lot of runs on my local machine and so I can't get stuff out, and the local dev environment is complex. So instead, I spent a lot of my time rewriting the whole internal guts of this so that all the data is in Cloud Storage, running on Cloud Functions. I was already half runninig on Cloud Functions but asumed that I downloaded all the data locally to script it. But instead, I've rewritten everything to be runnable locally, but actually natively in the cloud. So while I run some command line scripts on my machine, the real work is being done using Cloud Storage.</p>
<p>I'm pretty happy now that at this point, everything from the crawling, data classification, data generation and data labelling and machine learning model training is now in the cloud, nearly nothing is local on my machine except for the web app. Even that can be on the cloud, except it is still much faster to have hot-reload using Vue JS.</p>
<p>I've also scratched a really big visualization itch which has got me to learn how to be more proficient at using Three JS, but also overlay Three JS on Mapping APIs to create really cool visualizations.</p>
<p>Right now, I've built some really great tools, now I need to use them to train a machine learning model to help me classify GPX traces for mountains in order for me to produce a data-based summary of mountains.</p>
<h2>Blur App</h2>
<p>Also quickly hacked up a image blurring web app on a whim. I got quite a lot of play on Twitter and Instagram, though I'm not good a generating buzz and making things into a real app. I need to get better at that, figure out how to put things on to Product Hunt and getting some Google Juice out of it.</p>
<p>Wherecam is kind of like that too, it works great for me, it's public, but I don't know how to promote it.</p>]]></description>
      </item>
      <item>
        <title>Personal Microservices</title>
        <link>https://liquidx.net/posts/2021/personal-microservices</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2021/personal-microservices</guid>
        <pubDate>Tue, 04 May 2021 00:00:00 GMT</pubDate>
        <description><![CDATA[<p>I'm moving a lot of logic from my local machine into small functions that run on the "cloud" to do my bidding.</p>
<p>Example: This site you're reading this on, is a single Git repository, on GitHub. On each push/merge, it will trigger a site regeneration using GitHub Actions, then deploys the site to Firebase.</p>
<p>This allows me to use whatever tool to create content, whether it's VSCode on my desktop, GitHub's Web UI. In the near-future, I can write another micro service that takes email from myself and create git commits to my blog.</p>
<p>These multiple node workflows have existed for a long time, but usually within a single monolithic system. It mirrors the microservices trend of more distributed computing in commercial settings.</p>
<p>A few thoughts about this</p>
<ol>
<li>Lots of micro-tasks scattered around the internet makes it hard to know when something has failed. There's a lack of tools for managing this. Even if I try consolidating with one provider like Google/Firebase, the monitoring and control tools are woefully insufficient.</li>
<li>There is no good way to visualize how this is all working. There needs to be something like a Quartz Composer spaghetti monster equivalent that shows you how all the pieces are interconnected and for you to be able to create a new nodes and redirect inputs and outputs.</li>
<li>Authentication and impersonation is difficult. In order for these microservices to act for me, they need to assume my identity. That's hard to get right, and remembering not checking in your credentials and taking care of keys is not straightforward.</li>
</ol>]]></description>
      </item>
      <item>
        <title>Wherecam</title>
        <link>https://liquidx.net/posts/2021/wherecam</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2021/wherecam</guid>
        <pubDate>Sun, 02 May 2021 00:00:00 GMT</pubDate>
        <description><![CDATA[<p><img src="https://content.liquidx.net/media/posts/2021-05-03-1501.jpg" alt="Examples of Wherecam"></p>
<p>I love discovering places by reading about them on magazines and travel guides. If I find an interesting place, I want to see it on Google Maps to get more information, see where it is and save it for later.</p>
<p>Today, I have to manually type this in to Google Maps' search box (extra hard if I only have the Japanese name). Wouldn't it be nice if I could just scan the page and search Google Maps all in one go?</p>
<p>That's what Wherecam does. With Wherecam, you take a photo of the name of the place that's on a magazine or travel guide, and it does the rest. It'll show a snippet of the place and let you jump straight to the place in Google Maps.</p>
<p><strong>That's it.</strong></p>
<p>Try this out at <strong><a href="https://wherecam.web.app/">wherecam.web.app</a></strong> — and if you find it useful, save it to your home screen and it behaves like a normal iOS or Android app.</p>
<p>Let me know on Twitter at <a href="https://twitter.com/liquidx">@liquidx</a> what you think.</p>
<p><em>Continue on for more tech-y details.</em></p>
<p>The implementation is straightforward, it uses the <a href="https://cloud.google.com/vision/docs/features-list">Google Cloud Vision API</a> and <a href="https://developers.google.com/maps/documentation/places/web-service/overview">Google Maps Places API</a>, hosted on <a href="https://cloud.google.com/functions/docs/concepts/nodejs-runtime">Google Cloud Functions</a> and served off <a href="https://firebase.google.com/docs/hosting">Firebase Hosting</a>. The Cloud Function is a single NodeJS function that accepts an image in a POST request and uses Cloud Vision's Text Detection and <a href="https://developers.google.com/maps/documentation/places/web-service/search">Google Maps Find A Place by Text API</a> to return back a place given the text on the screen with incredibly simple heuristics.</p>
<p>The web app itself is about 400 lines of Javascript, and the Node JS Cloud Function is another 300 lines.</p>
<p>This all works because the whole app relies on the smartness behind the Google APIs. Cloud Vision is incredibly good at text detection, working across many languages, and able to detect text at any reasonable angle or distortion, including vertical text. The Place Search is forgiving too, able to understand partial names, mistyped names and able to come up with the most plausible place no matter where you are.</p>]]></description>
      </item>
      <item>
        <title>Words</title>
        <link>https://liquidx.net/posts/2021/words</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2021/words</guid>
        <pubDate>Sat, 01 May 2021 00:00:00 GMT</pubDate>
        <description><![CDATA[<p>Herein lies my revived blog of words.</p>
<p>I first started blogging in 2002, then stopped in 2012.
I had a short foray into publishing on <a href="http://medium.com/@liquidx/">Medium</a> in 2017.
My most popular posts about the made up rules observed in <a href="https://medium.com/japan-init/elevator-etiquette-in-japan-3cab23474e8c">Japanese Elevator Etiquette</a> was my real last piece of wirting.</p>
<p>But at work, writing is nearly all that I do. I'm writing emails. I'm writing proposals. I'm writing performance reviews. I'm writing design docs. I'm always writing at work, but rarely for fun.</p>
<p>So here in lies my attempt to write more for fun.</p>]]></description>
      </item>
      <item>
        <title>EV and Range</title>
        <link>https://liquidx.net/posts/2021/ev-and-range</link>
        <guid isPermaLink="true">https://liquidx.net/posts/2021/ev-and-range</guid>
        <pubDate>Wed, 31 Mar 2021 00:00:00 GMT</pubDate>
        <description><![CDATA[<p>My current favorite car, the Honda-e has a reported range of 259km (lowest number). For the weekend, I decided to rent one Honda EveryGo and see if I could do a road trip. The destination, the eastern most point in Chiba Prefecture.
<img src="https://content.liquidx.net/media/posts/2021-03-31-ev-and-range-2dc80f6e.png" alt="honda_e_price.png"></p>
<p>The car started at 98% full, the whole trip was about 295km total distance. I charged the car twice using a fast charger for 30 mins each (both getting from about 40% to 80%). I got the car home at 13% battery remaining. This means I went through nearly 1.8 times the battery capacity.</p>
<p>So what is the range? It definitely is not 259km. On the last leg, I got from 85% to 13% (72% used) over 114km, which extrapolates to about 150km range on a full charge to empty. (The numbers probably are lying to me though about capacity just like a regular petrol car lies about the fuel tank though.)</p>
<p>It's definitely something a little stressful about driving an EV with a short range on a road trip. There were a lot of charging stations en-route, so stopping to charge was easy, finding something to do while it was charging is a little harder. All the chargers would do 30-min increments.</p>
<p>There are also "slow" chargers — it reportedly wanted to take 7 hours to charge the car up from about 50%. I let it charge for 15 mins while we went for a stroll down the beach, came back to it only increasing the charge by 2%.</p>
<p>Road trips are very do-able though even with a 150km range, you'd likely want to plan to hit a charger every 100km and take a break.</p>]]></description>
      </item>
  </channel>
</rss>