<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>JavaScript on Wayne X.Y. Blog</title>
    <link>/en/tags/javascript/</link>
    <description>Recent content in JavaScript on Wayne X.Y. Blog</description>
    <generator>Hugo</generator>
    <language>en</language>
    <lastBuildDate>Tue, 05 May 2026 00:00:00 +0000</lastBuildDate>
    <atom:link href="/en/tags/javascript/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>VocabularyTest: Building My Own English Vocabulary Practice Tool</title>
      <link>/en/2026/05/05/vocabulary-test/</link>
      <pubDate>Tue, 05 May 2026 00:00:00 +0000</pubDate>
      <guid>/en/2026/05/05/vocabulary-test/</guid>
      <description>&lt;h2 id=&#34;why-build-this&#34;&gt;Why Build This?&lt;/h2&gt;&#xA;&lt;p&gt;Vocabulary memorization is probably the most tedious part of learning English. There are plenty of apps out there, but they all seem to have the same problems — too many ads, overcomplicated interfaces, or no way to practice the specific words you care about.&lt;/p&gt;&#xA;&lt;p&gt;So I built my own.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;&lt;a href=&#34;https://vocabulary-test.wayne-xy.com/&#34;&gt;VocabularyTest&lt;/a&gt;&lt;/strong&gt; is my latest side project: a lightweight English vocabulary quiz web app with one goal — open it, practice, and move on.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Adding an Interactive Travel Map to Hugo Blog (Leaflet &#43; JSON)</title>
      <link>/en/2026/03/10/travel-map-leaflet-hugo/</link>
      <pubDate>Tue, 10 Mar 2026 00:00:00 +0000</pubDate>
      <guid>/en/2026/03/10/travel-map-leaflet-hugo/</guid>
      <description>&lt;h1 id=&#34;adding-an-interactive-travel-map-to-hugo-blog-leaflet--json&#34;&gt;Adding an Interactive Travel Map to Hugo Blog (Leaflet + JSON)&lt;/h1&gt;&#xA;&lt;p&gt;Recently, I wanted to add an interactive map to the top of my blog&amp;rsquo;s Travel page to mark the places I&amp;rsquo;ve visited. After some consideration, I decided to use the open-source and lightweight Leaflet.js, paired with a custom JSON file to manage the locations and marker colors.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;-implementation-goals&#34;&gt;🗺️ Implementation Goals&lt;/h2&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Pin a map to the top of &lt;a href=&#34;/en/life/travel/&#34;&gt;&lt;code&gt;/life/travel&lt;/code&gt;&lt;/a&gt; to display the countries and places I&amp;rsquo;ve visited.&lt;/li&gt;&#xA;&lt;li&gt;Centrally manage location data (including English and Chinese names, latitude, longitude, and custom colors) via a JSON file.&lt;/li&gt;&#xA;&lt;li&gt;Use Leaflet.js to render the map, and automatically calculate the zoom boundaries (&lt;code&gt;fitBounds&lt;/code&gt;) based on the JSON data to perfectly fit all markers.&lt;/li&gt;&#xA;&lt;li&gt;Encapsulate the map into a Hugo Shortcode &lt;code&gt;&lt;link rel=&#34;stylesheet&#34; href=&#34;https://unpkg.com/leaflet@1.9.4/dist/leaflet.css&#34;&#xA;    integrity=&#34;sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=&#34; crossorigin=&#34;&#34; /&gt;&#xA;&lt;script src=&#34;https://unpkg.com/leaflet@1.9.4/dist/leaflet.js&#34;&#xA;    integrity=&#34;sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=&#34; crossorigin=&#34;&#34;&gt;&lt;/script&gt;&#xA;&#xA;&lt;div id=&#34;travel-map&#34; style=&#34;height: 400px; width: 100%; border-radius: 8px; margin-bottom: 2rem; z-index: 1;&#34;&gt;&lt;/div&gt;&#xA;&#xA;&lt;script&gt;&#xA;    document.addEventListener(&#34;DOMContentLoaded&#34;, function () {&#xA;        &#xA;        delete L.Icon.Default.prototype._getIconUrl;&#xA;        L.Icon.Default.mergeOptions({&#xA;            iconRetinaUrl: &#39;https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon-2x.png&#39;,&#xA;            iconUrl: &#39;https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon.png&#39;,&#xA;            shadowUrl: &#39;https://unpkg.com/leaflet@1.9.4/dist/images/marker-shadow.png&#39;,&#xA;        });&#xA;&#xA;        var travelData = [{&#34;color&#34;:&#34;red&#34;,&#34;lat&#34;:25.033,&#34;lng&#34;:121.5654,&#34;name_en&#34;:&#34;Taipei (Taiwan)&#34;,&#34;name_zh&#34;:&#34;台北 (台灣)&#34;},{&#34;color&#34;:&#34;red&#34;,&#34;lat&#34;:33.5902,&#34;lng&#34;:130.4017,&#34;name_en&#34;:&#34;Fukuoka City&#34;,&#34;name_zh&#34;:&#34;福岡市&#34;},{&#34;color&#34;:&#34;red&#34;,&#34;lat&#34;:33.2662,&#34;lng&#34;:131.3537,&#34;name_en&#34;:&#34;Yufuin&#34;,&#34;name_zh&#34;:&#34;由布院&#34;},{&#34;color&#34;:&#34;red&#34;,&#34;lat&#34;:33.2794,&#34;lng&#34;:131.5015,&#34;name_en&#34;:&#34;Beppu&#34;,&#34;name_zh&#34;:&#34;別府&#34;},{&#34;color&#34;:&#34;red&#34;,&#34;lat&#34;:33.173,&#34;lng&#34;:131.226,&#34;name_en&#34;:&#34;Kokonoe&#34;,&#34;name_zh&#34;:&#34;九重&#34;},{&#34;color&#34;:&#34;red&#34;,&#34;lat&#34;:33.0805,&#34;lng&#34;:131.1441,&#34;name_en&#34;:&#34;Kurokawa Onsen&#34;,&#34;name_zh&#34;:&#34;黑川溫泉&#34;},{&#34;color&#34;:&#34;red&#34;,&#34;lat&#34;:32.8062,&#34;lng&#34;:130.7058,&#34;name_en&#34;:&#34;Kumamoto&#34;,&#34;name_zh&#34;:&#34;熊本&#34;},{&#34;color&#34;:&#34;red&#34;,&#34;lat&#34;:33.1654,&#34;lng&#34;:130.4137,&#34;name_en&#34;:&#34;Yanagawa&#34;,&#34;name_zh&#34;:&#34;柳川&#34;},{&#34;color&#34;:&#34;red&#34;,&#34;lat&#34;:33.5215,&#34;lng&#34;:130.5348,&#34;name_en&#34;:&#34;Dazaifu&#34;,&#34;name_zh&#34;:&#34;太宰府&#34;}];&#xA;    var lang = &#34;en&#34;;&#xA;&#xA;    var map = L.map(&#39;travel-map&#39;, {&#xA;        scrollWheelZoom: true&#xA;    });&#xA;&#xA;    L.tileLayer(&#39;https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png&#39;, {&#xA;        attribution: &#39;&amp;copy; &lt;a href=&#34;https://www.openstreetmap.org/copyright&#34;&gt;OpenStreetMap&lt;/a&gt; contributors&#39;&#xA;    }).addTo(map);&#xA;&#xA;    var bounds = [];&#xA;&#xA;    if (travelData &amp;&amp; travelData.length &gt; 0) {&#xA;        travelData.forEach(function (loc) {&#xA;            var name = lang === &#34;en&#34; ? loc.name_en : loc.name_zh;&#xA;            var markerOptions = {};&#xA;            if (loc.color) {&#xA;                markerOptions.icon = new L.Icon({&#xA;                    iconUrl: &#39;https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-&#39; + loc.color + &#39;.png&#39;,&#xA;                    shadowUrl: &#39;https://unpkg.com/leaflet@1.9.4/dist/images/marker-shadow.png&#39;,&#xA;                    iconSize: [25, 41],&#xA;                    iconAnchor: [12, 41],&#xA;                    popupAnchor: [1, -34],&#xA;                    shadowSize: [41, 41]&#xA;                });&#xA;            }&#xA;            var marker = L.marker([loc.lat, loc.lng], markerOptions).addTo(map)&#xA;                .bindPopup(&#34;&lt;b&gt;&#34; + name + &#34;&lt;/b&gt;&#34;);&#xA;            bounds.push([loc.lat, loc.lng]);&#xA;        });&#xA;        map.fitBounds(bounds, { padding: [50, 50] });&#xA;    } else {&#xA;        map.setView([23.6978, 120.9605], 5); &#xA;    }&#xA;});&#xA;&lt;/script&gt;&lt;/code&gt; to easily reuse it across different language pages.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h2 id=&#34;-creating-the-data-file-data-layer&#34;&gt;💾 Creating the Data File (Data Layer)&lt;/h2&gt;&#xA;&lt;p&gt;First, create a &lt;code&gt;travel.json&lt;/code&gt; file under the &lt;code&gt;data/&lt;/code&gt; directory of your Hugo project. If you have new trips in the future, you just need to update this file without touching any code:&lt;/p&gt;</description>
    </item>
    <item>
      <title>Building a Micro-World: Playground Pixel Art and Pathfinding Implementation</title>
      <link>/en/2026/02/26/playground-pixel-art-implementation/</link>
      <pubDate>Thu, 26 Feb 2026 00:00:00 +0000</pubDate>
      <guid>/en/2026/02/26/playground-pixel-art-implementation/</guid>
      <description>&lt;h1 id=&#34;building-a-micro-world-playground-pixel-art-and-pathfinding-implementation&#34;&gt;Building a Micro-World: Playground Pixel Art and Pathfinding Implementation&lt;/h1&gt;&#xA;&lt;p&gt;&#xA;  &lt;img src=&#34;/img/playground/playground_overview.png&#34; alt=&#34;Playground Overview&#34;&gt;&#xA;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Recently, while coding, I came across an interesting project on GitHub called &lt;a href=&#34;https://github.com/pablodelucca/pixel-agents&#34;&gt;Pixel Agents&lt;/a&gt;. It simulates AI agents during coding as tiny pixel-style characters working in an office, which looks incredibly cute 😆. Because I really love pixel art, I wondered if I could bring a similar vibe to my own website. That&amp;rsquo;s how this &lt;a href=&#34;/en/playground/&#34;&gt;Playground&lt;/a&gt; came to be.&lt;/p&gt;&#xA;&lt;p&gt;This post will share the implementation details behind this little world, covering how to render crisp pixel art using HTML5 Canvas, designing the states for the characters, and how they navigate the room (BFS pathfinding algorithm).&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
