<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Timati</title>
    <description>The latest articles on DEV Community by Timati (@__1d4febcc4).</description>
    <link>https://dev.to/__1d4febcc4</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4024545%2F973eef31-3c25-475d-9912-2c6f59e6a2a1.png</url>
      <title>DEV Community: Timati</title>
      <link>https://dev.to/__1d4febcc4</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/__1d4febcc4"/>
    <language>en</language>
    <item>
      <title>How I Built a Tsunami Simulator on Real Terrain with Mapbox and OpenStreetMap</title>
      <dc:creator>Timati</dc:creator>
      <pubDate>Sun, 15 Nov 2026 16:58:26 +0000</pubDate>
      <link>https://dev.to/__1d4febcc4/how-i-built-a-tsunami-simulator-on-real-terrain-with-mapbox-and-openstreetmap-1pd2</link>
      <guid>https://dev.to/__1d4febcc4/how-i-built-a-tsunami-simulator-on-real-terrain-with-mapbox-and-openstreetmap-1pd2</guid>
      <description>&lt;p&gt;Most disaster maps tell you where something &lt;em&gt;did&lt;/em&gt; happen. I wanted one that answers a different question: &lt;strong&gt;what would happen on my street?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So I built &lt;a href="https://hazardsim.com/" rel="noopener noreferrer"&gt;HazardSim&lt;/a&gt;: you click a coastline, pick a wave height, and watch the water move inland over real terrain. It also does sea level rise and meteor impacts, and there are pages for earthquakes, hurricanes and volcanoes too.&lt;/p&gt;

&lt;p&gt;This post is about how it works under the hood, and just as importantly, where it is &lt;em&gt;wrong on purpose&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The data
&lt;/h2&gt;

&lt;p&gt;Three public sources do most of the work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Elevation:&lt;/strong&gt; Mapbox Terrain-RGB tiles, at roughly 10 to 30 m resolution. Each pixel encodes a height in its RGB channels. The documented decoding is:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Mapbox Terrain-RGB: height in metres&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;R&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="no"&gt;G&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="no"&gt;B&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;City boundaries:&lt;/strong&gt; OpenStreetMap. When you click the map, the site finds the city under the cursor and outlines its real boundary. That polygon becomes the calculation area.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Population:&lt;/strong&gt; OSM tags, with a manual density slider as a fallback when a place has no data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The nice side effect of using real boundaries is that the output is not "a circle on a map". The flooded area respects the actual city limits.&lt;/p&gt;

&lt;h2&gt;
  
  
  The physics (intentionally simple)
&lt;/h2&gt;

&lt;p&gt;I did not try to build a hydrodynamic solver. The rule is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Water enters from the sea.&lt;/li&gt;
&lt;li&gt;It floods every cell whose ground elevation is below the current water level.&lt;/li&gt;
&lt;li&gt;Every 250 m of travel, the wave loses 1 m of height. This one number stands in for friction against terrain and buildings.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In pseudocode, the idea looks like this (a simplified illustration, not the production code):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// BFS flood from the coastline&lt;/span&gt;
&lt;span class="n"&gt;queue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;coastCells&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;waveHeight&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;level&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shift&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;ground&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;          &lt;span class="c1"&gt;// terrain blocks the water&lt;/span&gt;
  &lt;span class="n"&gt;flooded&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;level&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;metresPerStep&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;250&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;     &lt;span class="c1"&gt;// lose 1 m per 250 m&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nf"&gt;neighbours&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;flooded&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hills, embankments and levees stop the wave, just as in reality. Water flows around obstacles and fills low ground, which is exactly what you want from a first-order tool.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sea level rise&lt;/strong&gt; uses the same engine but without the decay: the water simply sits at the chosen mark everywhere it can reach.&lt;/p&gt;

&lt;h2&gt;
  
  
  Meteors
&lt;/h2&gt;

&lt;p&gt;The meteor is a body of stone, iron, ice or loose rubble at a chosen entry speed (17 km/s by default). Damage radii come from standard overpressure scaling, and the crater is roughly twenty times wider than the body itself. Unlike tsunamis, meteors have no coastline restriction: you can drop one anywhere on Earth.&lt;/p&gt;

&lt;h2&gt;
  
  
  One check I liked adding
&lt;/h2&gt;

&lt;p&gt;Tsunamis and sea level rise need a coast, so before calculating, the site checks for open sea within about 30 km of your click, using the same elevation data. If there is none, it tells you instead of producing nonsense.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it gets wrong (on purpose)
&lt;/h2&gt;

&lt;p&gt;This is the part I care about most. The output is an &lt;strong&gt;order of magnitude&lt;/strong&gt;, not a forecast:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A real tsunami depends on seafloor shape, the fault that moved, the tide and the time of day. The tool ignores all of that. It answers: &lt;em&gt;if water of this height arrived here, how far inland would the terrain let it go?&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Casualty numbers assume &lt;strong&gt;nobody evacuated&lt;/strong&gt;, which never happens in real life.&lt;/li&gt;
&lt;li&gt;The decay rule is a single constant, not a fluid simulation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I would rather ship a simple model with honest labels than a flashy one that looks authoritative.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I learned
&lt;/h2&gt;

&lt;p&gt;The same wave height gives wildly different results depending on only a few metres of elevation. A 10 m wave barely troubles a city on high ground, while flat deltas are devastated. Seeing that on a map is more convincing than any table.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is next
&lt;/h2&gt;

&lt;p&gt;Earthquake, hurricane and volcano pages exist, and I recently added an ice age map showing the ocean 130 m lower than today. There is also an Android app on Google Play.&lt;/p&gt;

&lt;p&gt;If you try it, I would love feedback, especially on the model and on places where the terrain or boundary data misbehaves: &lt;a href="https://hazardsim.com/" rel="noopener noreferrer"&gt;hazardsim.com&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>webdev</category>
      <category>maps</category>
      <category>dataviz</category>
    </item>
    <item>
      <title>Show Dev: CountryClue – A fast and minimalist world flag guessing game</title>
      <dc:creator>Timati</dc:creator>
      <pubDate>Fri, 10 Jul 2026 21:41:43 +0000</pubDate>
      <link>https://dev.to/__1d4febcc4/show-dev-countryclue-a-fast-and-minimalist-world-flag-guessing-game-4a7j</link>
      <guid>https://dev.to/__1d4febcc4/show-dev-countryclue-a-fast-and-minimalist-world-flag-guessing-game-4a7j</guid>
      <description>&lt;p&gt;Hi everyone! 👋 I’ve built &lt;a href="https://countryclue.online/" rel="noopener noreferrer"&gt;CountryClue&lt;/a&gt;, a lightweight web game for guessing world flags.&lt;/p&gt;

&lt;p&gt;I focused heavily on zero-dependency performance and smooth UI animations, trying to keep it as fast and clean as possible. &lt;/p&gt;

&lt;p&gt;Please check out &lt;a href="https://countryclue.online/" rel="noopener noreferrer"&gt;the game&lt;/a&gt; and let me know if the flag rendering feels instant on your device!&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
