<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>vs slavchev&#x27;s cyberspace</title>
  <subtitle>Personal website of Veselin Slavchev — blog posts on DIY keyboards, electronics, databases, and programming projects.</subtitle>
  <link rel="alternate" type="text/html" href="https://vsslavchev.com/"/>
  <link rel="self" type="application/atom+xml" href="https://vsslavchev.com/feed.xml"/>
  <id>https://vsslavchev.com/</id>
  <updated>2026-05-05T00:00:00Z</updated>
  <author>
    <name>Veselin Slavchev</name>
    <uri>https://vsslavchev.com/</uri>
  </author>
  <entry>
    <title type="html">DIY keyboard, part 3: rebinding keys wirelessly over BLE</title>
    <link rel="alternate" type="text/html" href="https://vsslavchev.com/blog/diy-keyboard-ble-rebinding/"/>
    <id>https://vsslavchev.com/blog/diy-keyboard-ble-rebinding/</id>
    <published>2026-05-05T00:00:00Z</published>
    <updated>2026-05-05T00:00:00Z</updated>
    <summary>How I added a second BLE GATT service to my ESP32 keyboard so keys can be rebound wirelessly from a Web Bluetooth app, with layouts saved in flash.</summary>
    <content type="html">&lt;p&gt;Previous: &lt;a href=&quot;https://vsslavchev.com/blog/diy-keyboard-key-matrix/&quot;&gt;part 2, scanning the key matrix and fixing ghosting&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If I wanted to rebind a key on my custom keyboard I needed to reprogram it: open the keyboard case, connect with a cable,
   edit the firmware source, compile, and
  flash the board. But when I made two keyboards for friends they wanted to rebind some keys too, and that was a problem.
  I imagined having a nice UI for the task and making it even a fun experience. There is something special in taking care
  of your comfort and knowing that a minor improvement will pay off in the long run.
&lt;/p&gt;

&lt;p&gt;The solution also needed to be wireless and since the keyboard was already using BLE I only had to extend it. But first I
  had to learn how it really works. The way devices find each other is through
  advertising: a peripheral (the keyboard) repeatedly broadcasts small packets saying it exists. A central (the PC) scans for these packets
  and can initiate a connection. Once connected, both sides stop advertising and communicate through a client-server model called GATT
  (Generic Attribute Profile).&lt;/p&gt;

&lt;p&gt;GATT structures data as characteristics contained inside services. A service groups related functionality, and each characteristic in it
  is a value that can be read, written, or subscribed to for notifications. A keyboard exposes a standard HID (Human Interface Device) service. 
  At connect time the PC reads a report descriptor that tells it the shape of the data — in this case an 8-byte struct: one byte for modifier keys 
  (Ctrl, Shift, Alt, GUI), one reserved byte, and six slots for simultaneously held key HID codes. Every time a key is pressed or released 
  the keyboard sends an updated report via a GATT notification and the PC translates it into key events.&lt;/p&gt;

&lt;p&gt;The ESP32 microcontroller has BLE built in and the Arduino BLE library provides all the GATT plumbing. The keyboard object sets up a 
  &lt;code&gt;BLEHIDDevice&lt;/code&gt; with the standard keyboard report descriptor.
  When a key is pressed the firmware resolves the physical position through a matrix scan, looks up the keycode in the active layout level, and calls
  &lt;code&gt;keyboard.press(keycode)&lt;/code&gt; which updates the 8-byte report and notifies the host.&lt;/p&gt;

&lt;p&gt;The key rebinding part is a second BLE service sitting alongside the HID one. It has three characteristics:&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;A readable characteristic that returns the current layout as 192 bytes (2 layers × 4 rows × 12 keys × 2 bytes per key — one for the keycode, one for an optional modifier).&lt;/li&gt;
    &lt;li&gt;A writable data characteristic that accepts chunks of new layout bytes, so the whole layout can be sent in
      small BLE packets.&lt;/li&gt;
    &lt;li&gt;A writable control characteristic that accepts commands: enter config mode (pauses HID output), commit (applies and saves the received layout),
      or exit (cancels and resumes normal typing).&lt;/li&gt;
  &lt;/ul&gt;

&lt;p&gt;Another technology I wanted to learn about was the NVS (Non-Volatile Storage). The layout is persisted in the ESP32&#x27;s NVS, a key-value flash store 
  that survives power cycles. So on boot the firmware reads the
  stored layout and validates it with a simple XOR checksum before applying it. If the checksum fails — for example after a firmware update that
  changes the layout size — the default layout is used instead.&lt;/p&gt;

&lt;p&gt;Security is worth thinking about for this new keyboard setup because we don&#x27;t want someone to change our keys. Normally BLE
  keyboards are open to pairing from any device in range. For our case: to pair a new device you have to physically hold a four-key combo on
  the leftmost column at the same time. While the combo is held the firmware opens advertising to all, the LEDs stay on as a visual indicator, and
  keypresses from the combo keys are suppressed so nothing reaches the PC. A reconnect from a paired device works normally without any combo.&lt;/p&gt;

&lt;p&gt;The web app that handles rebinding connects using the Web Bluetooth API, which lets a browser act as a BLE central directly — no native app
  or driver needed. The browser&#x27;s &lt;code&gt;navigator.bluetooth.requestDevice()&lt;/code&gt; shows the system Bluetooth picker filtered to devices advertising
  the config service UUID. After connecting, the app reads the full current layout from the keyboard, renders it as a 4×12 grid with one button per
  key, and lets you click any key to open a picker with all available keys. When you hit save the app sends an enter-config command, streams the
  192-byte layout in 18-byte chunks, and finishes with a commit command. The keyboard applies the new mapping immediately and writes
  it to flash. The web app only works in Chromium-based browsers since Firefox and Safari have not implemented the Web Bluetooth spec.&lt;/p&gt;

&lt;p&gt;The result is a keyboard that can be configured wirelessly with a webapp:
  &lt;a href=&quot;https://keyboard.vsslavchev.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;check it out!&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;References:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Web Bluetooth Community Group. &quot;Web Bluetooth specification.&quot; Accessed 5 May, 2026. &lt;a href=&quot;https://webbluetoothcg.github.io/web-bluetooth/&quot;&gt;https://webbluetoothcg.github.io/web-bluetooth/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Espressif. &quot;ESP32 Non-Volatile Storage Library.&quot; Accessed 5 May, 2026. &lt;a href=&quot;https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/storage/nvs_flash.html&quot;&gt;https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/storage/nvs_flash.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title type="html">Single-table design in DynamoDB</title>
    <link rel="alternate" type="text/html" href="https://vsslavchev.com/blog/single-table-design/"/>
    <id>https://vsslavchev.com/blog/single-table-design/</id>
    <published>2024-09-25T00:00:00Z</published>
    <updated>2024-09-25T00:00:00Z</updated>
    <summary>An introduction to single-table design in DynamoDB: modelling entities and their relationships with composite partition and sort keys in one table.</summary>
    <content type="html">&lt;p&gt;Single-table design is a way to model your data so that all your entities and their relationships are in a single table in DynamoDB.
  This allows querying data in a single request and avoiding joins as they are not supported by DynamoDB, and has other cost and performance benefits.&lt;/p&gt;

&lt;p&gt;To find an object in DB we need to query it using both its PK (partition key) and SK (sort key).
  &lt;br&gt;&lt;code&gt;
    PK: &quot;USER#123&quot; &lt;br&gt;
    SK: &quot;USER#123&quot;
  &lt;/code&gt;&lt;br&gt;
We must specify a sort key, so here both keys have the same value. This is the case for simple entities only.
The id of the user is prepended by the type of entity and a &#x27;#&#x27; to delimit them.&lt;/p&gt;

&lt;p&gt;Now if we want to specify the relation that a user can have a house, we can do that in the same table like so:
  &lt;br&gt;&lt;code&gt;
    PK: &quot;USER#123&quot; &lt;br&gt;
    SK: &quot;HOUSE#456&quot;
  &lt;/code&gt;
&lt;/p&gt;

&lt;p&gt;This enables us to find the house if we know both user id and house id. Also to find all houses owned by the user only using their id:
&lt;br&gt;&lt;code&gt;
  query conditions: PK = &quot;USER#123&quot; and SK beginsWith &quot;HOUSE#&quot;
&lt;/code&gt;
&lt;/p&gt;

&lt;p&gt;We can expand the house entry to contain info on the state of the house:
&lt;br&gt;&lt;code&gt;
  PK: &quot;USER#123&quot; &lt;br&gt;
  SK: &quot;HOUSE#IN_CONSTRUCTION#456&quot;
&lt;/code&gt;&lt;br&gt;
This way we can query all houses of this user, or only those in a specific state.
&lt;br&gt;&lt;code&gt;
  query conditions: PK = &quot;USER#123&quot; and SK beginsWith &quot;HOUSE#FINISHED#&quot;
&lt;/code&gt;&lt;br&gt;
This stacking can be done multiple times to allow range queries on different attributes. A drawback here is that to get a house by user id and house id, 
  we also need to know its state. This is why we need to know our access patterns before making the schema.&lt;/p&gt;

&lt;p&gt;Another technique to allow flexibility of queries is adding more indexes. DynamoDB supports multiple GSIs (global secondary indexes) per table. For example:
&lt;br&gt;&lt;code&gt;
  GSI1 PK: &quot;CITY#Sofia&quot;&lt;br&gt;
  GSI1 SK: &quot;HOUSE#456&quot;
&lt;/code&gt;&lt;br&gt;
We can now query all houses in a city without knowing the user id.
  The index stores its own copy of every item it contains, so it takes up extra storage.
  Which fields are copied (projected) is chosen when the index is created: only the PK and SK, a selection of fields, or all of them.
&lt;/p&gt;

&lt;p&gt;GSI overloading is a trick to serve more access patterns than you have global secondary indexes for, or than the per-table limit allows.
It&#x27;s good practice to keep the GSIs to a minimum to save on storage and write costs. 
We can do this by using the same GSI with a different PK and SK for each entity.&lt;/p&gt;

&lt;p&gt;To demonstrate this design process we can go through the steps as I did for &lt;a href=&quot;https://www.speedcook.org/&quot;&gt;SpeedCook&lt;/a&gt;. The entities are:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Users&lt;/li&gt;
  &lt;li&gt;Recipes&lt;/li&gt;
  &lt;li&gt;Unlocked recipes&lt;/li&gt;
  &lt;li&gt;Cooked meals&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And the properties and relations they have:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Recipes have details&lt;/li&gt;
  &lt;li&gt;Users have details&lt;/li&gt;
  &lt;li&gt;User has many unlocked recipes&lt;/li&gt;
  &lt;li&gt;User has many cooked meals&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The rough functionality we want is:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;CRUD users and recipes&lt;/li&gt;
  &lt;li&gt;Unlock recipe for user&lt;/li&gt;
  &lt;li&gt;List unlocked recipes for user&lt;/li&gt;
  &lt;li&gt;Record cooked meals and see their history&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Based on the above we need to come up with the access patterns for each entity:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;User
    &lt;ul&gt;
      &lt;li&gt;Get user profile&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Recipe
    &lt;ul&gt;
      &lt;li&gt;Get single recipe&lt;/li&gt;
      &lt;li&gt;Get all recipes that can be cooked: open + unlocked&lt;/li&gt;
      &lt;li&gt;Get all locked recipes in a dietary category when choosing what to unlock&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Unlocked recipe
    &lt;ul&gt;
      &lt;li&gt;Unlock a locked recipe (unique, only once for a single user)&lt;/li&gt;
      &lt;li&gt;Get all unlocked recipes for user&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Cooked meal
    &lt;ul&gt;
      &lt;li&gt;Complete a cooking of a recipe by a user, non-unique - a user can cook the same recipe multiple times&lt;/li&gt;
      &lt;li&gt;See history of completed meals&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now we have everything we need to specify our schema. For each access pattern we specify if it reads or writes or both,
  if the target is the main index or a global secondary one, what is the query and a note on how it will be used:&lt;/p&gt;
  &lt;div class=&quot;w3-responsive&quot;&gt;
  &lt;table class=&quot;w3-table-all&quot;&gt;
    &lt;tr&gt;
      &lt;th&gt;Access pattern&lt;/th&gt;
      &lt;th&gt;operation&lt;/th&gt;
      &lt;th&gt;target&lt;/th&gt;
      &lt;th&gt;PK / SK&lt;/th&gt;
      &lt;th&gt;Notes&lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;User CRUD&lt;/td&gt;
      &lt;td&gt;R/W&lt;/td&gt;
      &lt;td&gt;main&lt;/td&gt;
      &lt;td&gt;PK USER#user_id&lt;br&gt;
        SK USER#user_id
        &lt;/td&gt;
      &lt;td&gt;Unique entry&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Get single recipe&lt;/td&gt;
      &lt;td&gt;R/W&lt;/td&gt;
      &lt;td&gt;main&lt;/td&gt;
      &lt;td&gt;PK RECIPE#recipe_id&lt;br&gt;
        SK RECIPE#recipe_id
        &lt;/td&gt;
      &lt;td&gt;Unique entry;
        non-key attribs:
        name,
        category,
        accessLevel,
        cookingTime,
        imageName,
        ingredients, 
        spices,
        instructions
        &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Get unlocked recipe ids for user, order by time of unlocking&lt;/td&gt;
      &lt;td&gt;R&lt;/td&gt;
      &lt;td&gt;main&lt;/td&gt;
      &lt;td&gt;PK USER#user_id&lt;br&gt;
        SK begins_with(UNLOCK_TIME#)
        &lt;/td&gt;
      &lt;td&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Get all recipes that can be cooked in one or more categories: unlocked for user + open&lt;/td&gt;
      &lt;td&gt;R&lt;/td&gt;
      &lt;td&gt;GSI1&lt;/td&gt;
      &lt;td&gt;PK ACCESS#access_type&lt;br&gt;
        SK CATEGORY#category_id#recipe_id
        &lt;/td&gt;
      &lt;td&gt;three steps:
        &lt;ol&gt;
          &lt;li&gt;get unlocked recipe ids for user&lt;/li&gt;
          &lt;li&gt;get batchItem recipes by the recipe ids&lt;/li&gt;
          &lt;li&gt;get all open recipes&lt;/li&gt;
        &lt;/ol&gt;
        &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Get all locked (for user) recipes in a dietary category when choosing what to unlock&lt;/td&gt;
      &lt;td&gt;R&lt;/td&gt;
      &lt;td&gt;step 1: GSI1, step 2: main
        &lt;/td&gt;
      &lt;td&gt;step 1:
        PK ACCESS#access_type&lt;br&gt;
        SK CATEGORY#category_id#recipe_id,&lt;br&gt;
        step 2:&lt;br&gt;
        PK USER#user_id&lt;br&gt;
        SK begins_with(UNLOCK_TIME#)
        &lt;/td&gt;
      &lt;td&gt;two steps:&lt;br&gt;
        1. get all locked (attainable) recipes&lt;br&gt;
        2. get all unlocked recipe ids for user and remove them from the list of locked ones
        &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Unlock recipe for user&lt;/td&gt;
      &lt;td&gt;W&lt;/td&gt;
      &lt;td&gt;main&lt;/td&gt;
      &lt;td&gt;PK USER#user_id&lt;br&gt;
        SK UNLOCK_TIME#unlock_time
        &lt;/td&gt;
      &lt;td&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Complete a cooking of a recipe by a user&lt;/td&gt;
      &lt;td&gt;W&lt;/td&gt;
      &lt;td&gt;main&lt;/td&gt;
      &lt;td&gt;PK USER#user_id&lt;br&gt;
        SK COOKED_TIME#cooked_time
        &lt;/td&gt;
      &lt;td&gt;Not unique: the time keeps repeated cookings of the same recipe apart&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;See history of completed meals, order by time of cooking&lt;/td&gt;
      &lt;td&gt;R&lt;/td&gt;
      &lt;td&gt;main&lt;/td&gt;
      &lt;td&gt;PK USER#user_id&lt;br&gt;
        SK begins_with(COOKED_TIME#)
        &lt;/td&gt;
      &lt;td&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/table&gt;
  &lt;/div&gt;

  &lt;p&gt;References:&lt;/p&gt;
    &lt;ul&gt;
      &lt;li&gt;Dynobase. “DynamoDB single table design made easy.” Accessed 25 Sept, 2024. &lt;a href=&quot;https://dynobase.dev/dynamodb-single-table-design-tool&quot;&gt;https://dynobase.dev/dynamodb-single-table-design-tool&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;AWS. “Data modelling foundations in DynamoDB.” Accessed 25 Sept, 2024. &lt;a href=&quot;https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/data-modeling-foundations.html&quot;&gt;https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/data-modeling-foundations.html&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;AWS. “Best practices for using sort keys to organize data in DynamoDB.” Accessed 25 Sept, 2024. &lt;a href=&quot;https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/bp-sort-keys.html&quot;&gt;https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/bp-sort-keys.html&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title type="html">Animating SVG with simple JS</title>
    <link rel="alternate" type="text/html" href="https://vsslavchev.com/blog/animated-svg/"/>
    <id>https://vsslavchev.com/blog/animated-svg/</id>
    <published>2020-06-21T00:00:00Z</published>
    <updated>2020-06-21T00:00:00Z</updated>
    <summary>How to make an SVG look hand-drawn by gradually extending the stroke-dasharray of its paths with a few lines of plain JavaScript.</summary>
    <content type="html">&lt;button onclick=&quot;startDrawingPath()&quot; class=&quot;w3-button w3-round-large w3-border&quot;&gt;Run the animation again&lt;/button&gt;
&lt;svg viewBox=&quot;0 0 200 50&quot;&gt;
  &lt;path class=&quot;animatable&quot;
        d=&quot;M 8.1264881,15.974702 14.363095,35.251487 19.087797,15.407738&quot;
        style=&quot;fill:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1&quot;&gt;
  &lt;/path&gt;
  &lt;path class=&quot;animatable&quot;
          style=&quot;fill:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1&quot;
          d=&quot;M 33.072916,15.974702 C 4.643728,22.543248 49.083918,27.413766 22.300595,34.684522&quot;&gt;
  &lt;/path&gt;
  &lt;path class=&quot;animatable&quot;
          style=&quot;fill:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1&quot;
          d=&quot;m 112.05275,16.634265 0.46772,15.835719&quot;&gt;
  &lt;/path&gt;
  &lt;path class=&quot;animatable&quot;
          style=&quot;fill:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1&quot;
          d=&quot;m 118.66767,16.7679 c 0,0.334087 0.66817,15.234364 0.66817,15.234364&quot;&gt;
  &lt;/path&gt;
  &lt;path class=&quot;animatable&quot;
          style=&quot;fill:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1&quot;
          d=&quot;m 129.75935,16.634265 c -0.26727,0.133635 -7.34991,-0.200452 -7.34991,-0.200452 l 0.93545,15.167546 6.949,-1.469982&quot;&gt;
  &lt;/path&gt;
  &lt;path class=&quot;animatable&quot;
          style=&quot;fill:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1&quot;
          d=&quot;m 48.17107,30.998313 c 1.448659,9.593731 17.169091,-1.434365 4.192131,-6.648975 -10.40116,-3.849103 8.310748,-14.4767454 9.402157,-3.526162 0,0 0.522092,12.36668 0.900068,12.36668 0.377977,0 9.874628,0.236236 9.874628,0.236236 l 5.197173,-17.528646 7.276042,15.449777&quot;&gt;
  &lt;/path&gt;
  &lt;path class=&quot;animatable&quot;
          style=&quot;fill:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1&quot;
          d=&quot;m 74.477182,26.764261 c 0.614211,-0.141741 11.670014,-7.984747 11.670014,-7.984747 l 5.291666,14.93006 6.142114,-16.300224&quot;&gt;
  &lt;/path&gt;
  &lt;path class=&quot;animatable&quot;
          style=&quot;fill:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1&quot;
          d=&quot;m 107.82578,20.139337 c -13.490843,-15.9715525 -10.214265,28.79624 2.70611,6.280834 l 19.92582,-4.525402 5.10987,10.113912 5.14993,-14.93006&quot;&gt;
  &lt;/path&gt;
  &lt;path class=&quot;animatable&quot;
          style=&quot;fill:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1&quot;
          d=&quot;M 31.759931,34.553711 H 45.390675&quot;&gt;
  &lt;/path&gt;
&lt;/svg&gt;
&lt;p&gt;A nice looking effect can be achieved by gradually drawing a SVG. You can do this for an icon or some text that is made out of paths.
&lt;/p&gt;
&lt;p&gt;SVG files are just
  XML so you can use a SVG editor to make the paths and open the file with a text editor. When copying the tags to your HTML you should check if the code
  is also valid HTML, maybe you need to close the tags or delete some attributes.
&lt;/p&gt;
&lt;p&gt;You can fit this to your needs by having it also draw backwards by reducing the &lt;code&gt;length&lt;/code&gt; that is overwritten each tick on the &lt;code&gt;strokeDasharray&lt;/code&gt;.
 You can have this run when the page opens or an element is hovered or a menu text/icon is rendered.&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code&gt;/* original idea from: https://stackoverflow.com/a/14282391 by https://stackoverflow.com/users/405017/phrogz */

var percentageChangePerTick = 0.01;
var drawFPS = 60;

var paths = document.querySelectorAll(&#x27;path.animatable&#x27;);
var timer;

function startDrawingPath(){
  for (var i = 0; i &amp;lt; paths.length; i++) {
    paths[i].style.stroke = &#x27;#00FF66&#x27;;
    paths[i].style.strokeDasharray = 0;
  }
  clearInterval(timer);
  increaseLength();
  timer = setInterval(increaseLength,1000/drawFPS);
}

function increaseLength(){
  var finished = true;
  for (var i = 0; i &amp;lt; paths.length; i++) {
    var totalPathLength = paths[i].getTotalLength();
    var currentLength = parseFloat(paths[i].style.strokeDasharray.split(&quot;,&quot;)[0]);
    var newLength = currentLength + (percentageChangePerTick * totalPathLength);
    paths[i].style.strokeDasharray = [newLength, totalPathLength].join(&#x27; &#x27;);
    if (newLength &amp;lt;= totalPathLength) {
      finished = false;
    }
  }
  if (finished) {
    clearInterval(timer);
  }
}&lt;/code&gt;&lt;/pre&gt;
</content>
  </entry>
  <entry>
    <title type="html">Learning German vocabulary through concepts, not translations</title>
    <link rel="alternate" type="text/html" href="https://vsslavchev.com/blog/learning-german-vocabulary/"/>
    <id>https://vsslavchev.com/blog/learning-german-vocabulary/</id>
    <published>2020-05-03T00:00:00Z</published>
    <updated>2020-05-03T00:00:00Z</updated>
    <summary>Learn foreign vocabulary by tying words to concepts instead of translations, with the many meanings of the German pronouns sie, du, ihr and Sie as an example.</summary>
    <content type="html">&lt;p&gt;Learning words in a new language is best done when connecting the concept of something directly to the word in the new language - as
  opposed to mapping the new word to the one in your mother tongue which is in turn mapped to the concept.&lt;/p&gt;
&lt;p&gt;To help you and me form the habit of directly mapping the new word to the concept, I will show an example with words that makes
  this approach a necessity: the pronouns in the German language. German has multiple uses for &#x27;&lt;span lang=&quot;de&quot;&gt;sie&lt;/span&gt;&#x27; and in English we say &#x27;you&#x27; for many
  different cases, so this mismatch and repeatedly using the same word for different meanings is a good example.
&lt;/p&gt;
&lt;p&gt;So first let&#x27;s imagine or visualise the concepts:&lt;/p&gt;

&lt;div class=&quot;flex-row&quot;&gt;
    &lt;figure class=&quot;in-flex-card w3-card&quot;&gt;
      &lt;img loading=&quot;lazy&quot; width=&quot;512&quot; height=&quot;512&quot; src=&quot;https://vsslavchev.com/img/german_pronouns/you.svg&quot; alt=&quot;Single person being addressed&quot;&gt;
      &lt;figcaption class=&quot;w3-container&quot;&gt;Imagine talking to a friend. You are about to tell him something.&lt;/figcaption&gt;
    &lt;/figure&gt;

    &lt;figure class=&quot;in-flex-card w3-card&quot;&gt;
      &lt;img loading=&quot;lazy&quot; width=&quot;512&quot; height=&quot;511&quot; src=&quot;https://vsslavchev.com/img/german_pronouns/you_plural.svg&quot; alt=&quot;Multiple people being addressed&quot;&gt;
      &lt;figcaption class=&quot;w3-container&quot;&gt;Imagine talking to multiple people. &lt;/figcaption&gt;
    &lt;/figure&gt;

    &lt;figure class=&quot;in-flex-card w3-card&quot;&gt;
      &lt;img loading=&quot;lazy&quot; width=&quot;512&quot; height=&quot;512&quot; src=&quot;https://vsslavchev.com/img/german_pronouns/king.svg&quot; alt=&quot;A king, representing formal address&quot;&gt;
      &lt;figcaption class=&quot;w3-container&quot;&gt;Imagine speaking to the king. Address him formally.&lt;/figcaption&gt;
    &lt;/figure&gt;

    &lt;figure class=&quot;in-flex-card w3-card&quot;&gt;
      &lt;img loading=&quot;lazy&quot; width=&quot;512&quot; height=&quot;512&quot; src=&quot;https://vsslavchev.com/img/german_pronouns/they.svg&quot; alt=&quot;Group of people not in the conversation&quot;&gt;
      &lt;figcaption class=&quot;w3-container&quot;&gt;Imagine talking about a group of people who are not present in the conversation.&lt;/figcaption&gt;
    &lt;/figure&gt;

    &lt;figure class=&quot;in-flex-card w3-card&quot;&gt;

      &lt;img loading=&quot;lazy&quot; width=&quot;512&quot; height=&quot;512&quot; src=&quot;https://vsslavchev.com/img/german_pronouns/she.svg&quot; alt=&quot;A woman not in the conversation&quot;&gt;
      &lt;figcaption class=&quot;w3-container&quot;&gt;Imagine talking about a woman who is not present in the conversation.&lt;/figcaption&gt;
    &lt;/figure&gt;
&lt;/div&gt;

&lt;div class=&quot;w3-responsive center for-table&quot;&gt;
  &lt;br&gt;
  &lt;span lang=&quot;de&quot;&gt;Nominativ&lt;/span&gt;:
  &lt;table class=&quot;w3-table-all&quot;&gt;
    &lt;colgroup&gt;
      &lt;col span=&quot;1&quot; style=&quot;width: 70%;&quot;&gt;
      &lt;col span=&quot;1&quot; style=&quot;width: 30%;&quot;&gt;
    &lt;/colgroup&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;th&gt;Example sentence&lt;/th&gt;
        &lt;th&gt;Concept&lt;/th&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;div&gt;&lt;p lang=&quot;de&quot;&gt;Hast du das getan?&lt;/p&gt;&lt;/div&gt;&lt;/td&gt;
        &lt;td&gt;&lt;img loading=&quot;lazy&quot; width=&quot;512&quot; height=&quot;512&quot; src=&quot;https://vsslavchev.com/img/german_pronouns/you.svg&quot; style=&quot;width:100%&quot; alt=&quot;du (you, informal singular)&quot;&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;div&gt;&lt;p lang=&quot;de&quot;&gt;Habt ihr das getan?&lt;/p&gt;&lt;/div&gt;&lt;/td&gt;
        &lt;td&gt;&lt;img loading=&quot;lazy&quot; width=&quot;512&quot; height=&quot;511&quot; src=&quot;https://vsslavchev.com/img/german_pronouns/you_plural.svg&quot; style=&quot;width:100%&quot; alt=&quot;ihr (you, plural)&quot;&gt;&lt;/td&gt;
        &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;div&gt;&lt;p lang=&quot;de&quot;&gt;Haben Sie das getan?&lt;/p&gt;&lt;/div&gt;&lt;/td&gt;
        &lt;td&gt;&lt;img loading=&quot;lazy&quot; width=&quot;512&quot; height=&quot;512&quot; src=&quot;https://vsslavchev.com/img/german_pronouns/king.svg&quot; style=&quot;width:100%&quot; alt=&quot;Sie (you, formal)&quot;&gt;&lt;/td&gt;
        &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;div&gt;&lt;p lang=&quot;de&quot;&gt;Haben sie das getan?&lt;/p&gt;&lt;/div&gt;&lt;/td&gt;
        &lt;td&gt;&lt;img loading=&quot;lazy&quot; width=&quot;512&quot; height=&quot;512&quot; src=&quot;https://vsslavchev.com/img/german_pronouns/they.svg&quot; style=&quot;width:100%&quot; alt=&quot;sie (they)&quot;&gt;&lt;/td&gt;
        &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;div&gt;&lt;p lang=&quot;de&quot;&gt;Hat sie das getan?&lt;/p&gt;&lt;/div&gt;&lt;/td&gt;
        &lt;td&gt;&lt;img loading=&quot;lazy&quot; width=&quot;512&quot; height=&quot;512&quot; src=&quot;https://vsslavchev.com/img/german_pronouns/she.svg&quot; style=&quot;width:100%&quot; alt=&quot;sie (she)&quot;&gt;&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;
  &lt;br&gt;
  &lt;span lang=&quot;de&quot;&gt;Akkusativ&lt;/span&gt;:
  &lt;table class=&quot;w3-table-all&quot;&gt;
    &lt;colgroup&gt;
      &lt;col span=&quot;1&quot; style=&quot;width: 70%;&quot;&gt;
      &lt;col span=&quot;1&quot; style=&quot;width: 30%;&quot;&gt;
    &lt;/colgroup&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;th&gt;Example sentence&lt;/th&gt;
        &lt;th&gt;Concept&lt;/th&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;div&gt;&lt;p lang=&quot;de&quot;&gt;Ich sehe dich.&lt;/p&gt;&lt;/div&gt;&lt;/td&gt;
        &lt;td&gt;&lt;img loading=&quot;lazy&quot; width=&quot;512&quot; height=&quot;512&quot; src=&quot;https://vsslavchev.com/img/german_pronouns/you.svg&quot; style=&quot;width:100%&quot; alt=&quot;du (you, informal singular)&quot;&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;div&gt;&lt;p lang=&quot;de&quot;&gt;Ich sehe euch.&lt;/p&gt;&lt;/div&gt;&lt;/td&gt;
        &lt;td&gt;&lt;img loading=&quot;lazy&quot; width=&quot;512&quot; height=&quot;511&quot; src=&quot;https://vsslavchev.com/img/german_pronouns/you_plural.svg&quot; style=&quot;width:100%&quot; alt=&quot;ihr (you, plural)&quot;&gt;&lt;/td&gt;
        &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;div&gt;&lt;p lang=&quot;de&quot;&gt;Ich sehe Sie.&lt;/p&gt;&lt;/div&gt;&lt;/td&gt;
        &lt;td&gt;&lt;img loading=&quot;lazy&quot; width=&quot;512&quot; height=&quot;512&quot; src=&quot;https://vsslavchev.com/img/german_pronouns/king.svg&quot; style=&quot;width:100%&quot; alt=&quot;Sie (you, formal)&quot;&gt;&lt;/td&gt;
        &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;div&gt;&lt;p lang=&quot;de&quot;&gt;Ich sehe sie.&lt;/p&gt;&lt;/div&gt;&lt;/td&gt;
        &lt;td&gt;&lt;img loading=&quot;lazy&quot; width=&quot;512&quot; height=&quot;512&quot; src=&quot;https://vsslavchev.com/img/german_pronouns/they.svg&quot; style=&quot;width:100%&quot; alt=&quot;sie (they)&quot;&gt;&lt;/td&gt;
        &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;div&gt;&lt;p lang=&quot;de&quot;&gt;Ich sehe sie.&lt;/p&gt;&lt;/div&gt;&lt;/td&gt;
        &lt;td&gt;&lt;img loading=&quot;lazy&quot; width=&quot;512&quot; height=&quot;512&quot; src=&quot;https://vsslavchev.com/img/german_pronouns/she.svg&quot; style=&quot;width:100%&quot; alt=&quot;sie (she)&quot;&gt;&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;
  &lt;br&gt;
  &lt;span lang=&quot;de&quot;&gt;Dativ&lt;/span&gt;:
  &lt;table class=&quot;w3-table-all&quot;&gt;
    &lt;colgroup&gt;
      &lt;col span=&quot;1&quot; style=&quot;width: 70%;&quot;&gt;
      &lt;col span=&quot;1&quot; style=&quot;width: 30%;&quot;&gt;
    &lt;/colgroup&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;th&gt;Example sentence&lt;/th&gt;
        &lt;th&gt;Concept&lt;/th&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;div&gt;&lt;p lang=&quot;de&quot;&gt;Ich gebe dir etwas.&lt;/p&gt;&lt;/div&gt;&lt;/td&gt;
        &lt;td&gt;&lt;img loading=&quot;lazy&quot; width=&quot;512&quot; height=&quot;512&quot; src=&quot;https://vsslavchev.com/img/german_pronouns/you.svg&quot; style=&quot;width:100%&quot; alt=&quot;du (you, informal singular)&quot;&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;div&gt;&lt;p lang=&quot;de&quot;&gt;Ich gebe euch etwas.&lt;/p&gt;&lt;/div&gt;&lt;/td&gt;
        &lt;td&gt;&lt;img loading=&quot;lazy&quot; width=&quot;512&quot; height=&quot;511&quot; src=&quot;https://vsslavchev.com/img/german_pronouns/you_plural.svg&quot; style=&quot;width:100%&quot; alt=&quot;ihr (you, plural)&quot;&gt;&lt;/td&gt;
        &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;div&gt;&lt;p lang=&quot;de&quot;&gt;Ich gebe Ihnen etwas.&lt;/p&gt;&lt;/div&gt;&lt;/td&gt;
        &lt;td&gt;&lt;img loading=&quot;lazy&quot; width=&quot;512&quot; height=&quot;512&quot; src=&quot;https://vsslavchev.com/img/german_pronouns/king.svg&quot; style=&quot;width:100%&quot; alt=&quot;Sie (you, formal)&quot;&gt;&lt;/td&gt;
        &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;div&gt;&lt;p lang=&quot;de&quot;&gt;Ich gebe ihnen etwas.&lt;/p&gt;&lt;/div&gt;&lt;/td&gt;
        &lt;td&gt;&lt;img loading=&quot;lazy&quot; width=&quot;512&quot; height=&quot;512&quot; src=&quot;https://vsslavchev.com/img/german_pronouns/they.svg&quot; style=&quot;width:100%&quot; alt=&quot;sie (they)&quot;&gt;&lt;/td&gt;
        &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;div&gt;&lt;p lang=&quot;de&quot;&gt;Ich gebe ihr etwas.&lt;/p&gt;&lt;/div&gt;&lt;/td&gt;
        &lt;td&gt;&lt;img loading=&quot;lazy&quot; width=&quot;512&quot; height=&quot;512&quot; src=&quot;https://vsslavchev.com/img/german_pronouns/she.svg&quot; style=&quot;width:100%&quot; alt=&quot;sie (she)&quot;&gt;&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;
&lt;/div&gt;

&lt;p&gt;sources: &lt;br&gt;
    &lt;a href=&quot;https://en.wikibooks.org/wiki/German/Grammar/Pronouns&quot;&gt;https://en.wikibooks.org/wiki/German/Grammar/Pronouns&lt;/a&gt; &lt;br&gt;
    &lt;a href=&quot;https://german.stackexchange.com/a/48144&quot;&gt;https://german.stackexchange.com/a/48144&lt;/a&gt; 
&lt;/p&gt;
&lt;div&gt;Icons made by &lt;a href=&quot;https://www.flaticon.com/authors/freepik&quot; title=&quot;Freepik&quot;&gt;Freepik&lt;/a&gt;
  from &lt;a href=&quot;https://www.flaticon.com/&quot; title=&quot;Flaticon&quot;&gt;www.flaticon.com&lt;/a&gt;
&lt;/div&gt;
</content>
  </entry>
  <entry>
    <title type="html">Paul Otlet, Henri La Fontaine and the birth of information science</title>
    <link rel="alternate" type="text/html" href="https://vsslavchev.com/blog/paul-otlet-information-science/"/>
    <id>https://vsslavchev.com/blog/paul-otlet-information-science/</id>
    <published>2019-10-10T00:00:00Z</published>
    <updated>2019-10-10T00:00:00Z</updated>
    <summary>Paul Otlet and Henri La Fontaine, the Belgian pioneers of information science who set out to collect and catalogue the knowledge of the world.</summary>
    <content type="html">&lt;img loading=&quot;lazy&quot; width=&quot;551&quot; height=&quot;179&quot; class=&quot;w3-round-large w3-mobile w3-image&quot; src=&quot;https://vsslavchev.com/img/mundaneum.jpg&quot; alt=&quot;Photo of the Mundaneum building&quot;&gt;
&lt;p&gt;In the time of high attention to AI and data science we talk about the high value of data and how large amounts of it are precious. 
  Governments try to regulate how it is collected, shared and protected. But do you know who are the fathers of information science?&lt;/p&gt;
&lt;p&gt;I stumbled upon this wikipedia article of a Belgian who lived in the 19th and 20th centuries. The article had only 1400 visitors over the 
  last 30 days. I thought it was a pity that I had never heard of that man. His name was Paul Otlet and he wanted access to knowledge for 
  people. His friend and colleague Henri La Fontaine was the first president of the International Peace Bureau - the world’s oldest 
  international peace organisation, with 10 of its members having received a Nobel Peace prize - his article even less visited.&lt;/p&gt;
&lt;p&gt;In short Otlet got fascinated by sharing information and set up a service where cards containing text were carefully curated and 
  systematically queried. The amount of requests quickly grew in a matter of years. This is why I mentioned wikipedia earlier - it was 
  the same idea - just in 1896, and much slower. It was meant to be easy to use for searching information and used a concept similar to hyperlinks.&lt;/p&gt;
&lt;p&gt;The government of Belgium gave him buildings and budget to expand his efforts. But then the Great War started, where one of his sons 
  lost his life. Otlet believed that forming an international organisation that promotes knowledge sharing can prevent future wars and 
  he defined a charter of human rights.&lt;/p&gt;
&lt;p&gt;I felt strange that the information about this man is not more well known. And it makes me think not about the man, but about the 
  relationship we as a civilisation have with information. Its value over the centuries and our desire to ask for it and use it.&lt;/p&gt;
&lt;p&gt;Another thing I found interesting is that he was not stuck in his ways and beliefs. When new tech like radio and microfilm came 
  up he started incorporating it in his efforts. I can imagine how someone else would have seen these new changes as a threat.&lt;/p&gt;
&lt;p&gt;He did also have dreams of building a utopian city that would be the center of the knowledge of the world, but despite never 
  being even started he still worked on what he believed for the rest of his life.&lt;/p&gt;
&lt;p&gt;References:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Britannica. &quot;Paul Otlet&quot;. Accessed 22 Feb, 2025. &lt;a href=&quot;https://www.britannica.com/biography/Paul-Otlet&quot;&gt;https://www.britannica.com/biography/Paul-Otlet&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title type="html">DIY keyboard, part 2: scanning the key matrix and fixing ghosting</title>
    <link rel="alternate" type="text/html" href="https://vsslavchev.com/blog/diy-keyboard-key-matrix/"/>
    <id>https://vsslavchev.com/blog/diy-keyboard-key-matrix/</id>
    <published>2019-06-19T00:00:00Z</published>
    <updated>2019-06-19T00:00:00Z</updated>
    <summary>How a handwired Arduino keyboard detects key presses by scanning a key matrix, why ghosting happens, and how diodes prevent it.</summary>
    <content type="html">&lt;p&gt;Previous: &lt;a href=&quot;https://vsslavchev.com/blog/diy-keyboard-planning/&quot;&gt;part 1, planning a handwired, 3D-printed keyboard&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Because we don&#x27;t have enough inputs on the Arduino we can implement a key matrix to
  detect which keys are pressed. This way the number of inputs needed is the number of columns plus rows.&lt;/p&gt;
&lt;p&gt;The way it works is that we send a signal on one row and check which columns receive it. Then check the next row the same way.&lt;/p&gt;
&lt;p&gt;The basic idea of the matrix scanning:&lt;br&gt;
&lt;img loading=&quot;lazy&quot; width=&quot;574&quot; height=&quot;542&quot; class=&quot;w3-round-large w3-mobile w3-image center&quot; src=&quot;https://vsslavchev.com/img/keyboard/1_scanning.png&quot; alt=&quot;Diagram of key matrix scanning&quot;&gt;&lt;/p&gt;
&lt;p&gt;There is the problem called ghosting - keys which are not really pressed register as being pressed.
    Using diodes we can prevent the effect.&lt;/p&gt;
&lt;p&gt; Here is how it happens when diodes are not present:&lt;br&gt;
&lt;img loading=&quot;lazy&quot; width=&quot;590&quot; height=&quot;544&quot; class=&quot;w3-round-large w3-mobile w3-image&quot; src=&quot;https://vsslavchev.com/img/keyboard/2_scanning.webp&quot; alt=&quot;Diagram showing ghosting without diodes&quot;&gt;&lt;/p&gt;
&lt;p&gt;As you can see the right column does receive a signal when it is the top row&#x27;s turn to send it, because the signal goes 
  through the top-left pressed key to the left column, then through the bottom-left key to the bottom row, and then through the 
  bottom-right key to the right column. That means that the top right switch is registered as pressed, but it&#x27;s not.&lt;/p&gt;
&lt;p&gt;And here is the same scenario, but with diodes:&lt;br&gt;
&lt;img loading=&quot;lazy&quot; width=&quot;506&quot; height=&quot;577&quot; class=&quot;w3-round-large w3-mobile w3-image&quot; src=&quot;https://vsslavchev.com/img/keyboard/3_scanning.png&quot; alt=&quot;Diagram showing ghosting prevented with diodes&quot;&gt;&lt;/p&gt;
&lt;p&gt;It is a good idea to test by soldering using very little tin - just enough to hold on, instead of trying to fiddle with 
  holding them in place.&lt;/p&gt;
&lt;p&gt;I decided to switch the wiring from the blogs I read - my rows are connected directly and columns use diodes&lt;/p&gt;
&lt;p&gt;You can use this jewelry making pliers to make little loops that can go onto the switch legs or the wires. &lt;br&gt;
&lt;img loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;642&quot; class=&quot;w3-round-large w3-mobile w3-image&quot; src=&quot;https://vsslavchev.com/img/keyboard/diode_loops.jpg&quot; alt=&quot;Jewelry pliers making loops on diode legs&quot;&gt;&lt;/p&gt;
&lt;p&gt;The next problem to solve is debouncing - detecting that a key is pressed and released many times in a short time, 
  while in fact the key is only being pressed or released, but the contact between the metal plates in the switch is not 
  really solid while in the process of moving. After doing the research I found there are hardware and software ways to solve 
  this problem. The hardware one is with capacitors, but that makes it more of a hassle for me. There are multiple ways to solve 
  it with software:&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;waiting for a certain amount of time over which no change happens - this way we can be sure that the state is stable and 
      we can read this as the current situation - this is slower, but might not be noticeable&lt;/li&gt;
    &lt;li&gt;detecting the first change and registering that one, then ignoring changes for some time and checking the state after this 
      time - this is per key and is making the decision faster than the previous one&lt;/li&gt;
    &lt;li&gt;only checking for changes every 50 milliseconds, for example - this is fast and easy to do, but is on all keys&lt;/li&gt;
    &lt;li&gt;track the state over time and accumulate the values to calculate to which extreme it is more close - this is not 
      fast and might not be very robust without tuning&lt;/li&gt;
  &lt;/ul&gt;
&lt;p&gt;My first approach is to do it the simple way and put more effort when those results become unsatisfactory for my needs. 
  So I will only check the state of all keys every 25 milliseconds.&lt;/p&gt;
&lt;p&gt;The next thing to do is make a layout for our keys. I decided to have preprocessor macros that map a readable name to a 
  keycode of type byte. I can send these bytes to the receiving host without mapping them. The key names I can use to initialize 
  a 2D array which will represent how I want my keys to be ordered. When I know the position of a pressed key in terms of row 
  and column I can then access the resulting keycode through the array.&lt;/p&gt;
&lt;img loading=&quot;lazy&quot; width=&quot;706&quot; height=&quot;448&quot; class=&quot;w3-round-large w3-mobile w3-image&quot; src=&quot;https://vsslavchev.com/img/keyboard/code.png&quot; alt=&quot;Arduino firmware code for keyboard layout&quot;&gt;
&lt;p&gt;Later on this could become a 3D array when we need to have multiple layouts.&lt;/p&gt;
&lt;p&gt;Next: &lt;a href=&quot;https://vsslavchev.com/blog/diy-keyboard-ble-rebinding/&quot;&gt;part 3, rebinding keys wirelessly over BLE&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title type="html">DIY keyboard, part 1: planning a handwired, 3D-printed keyboard</title>
    <link rel="alternate" type="text/html" href="https://vsslavchev.com/blog/diy-keyboard-planning/"/>
    <id>https://vsslavchev.com/blog/diy-keyboard-planning/</id>
    <published>2019-06-10T00:00:00Z</published>
    <updated>2019-06-10T00:00:00Z</updated>
    <summary>Planning my first DIY keyboard after seeing an OLKB Planck: a 3D printed case, handwired switches, and the fancy ideas saved for a second version.</summary>
    <content type="html">&lt;img loading=&quot;lazy&quot; width=&quot;923&quot; height=&quot;338&quot; class=&quot;w3-round-large w3-mobile w3-image&quot; src=&quot;https://vsslavchev.com/img/keyboard/soldered.jpg&quot; alt=&quot;Photo of the handwired keyboard&quot;&gt;
&lt;p&gt;A colleague of mine has a fancy keyboard - the OLKB Planck. It is sold as a DIY kit that you assemble and program yourself. 
  I was unsure how I feel about it when I first saw it. But his explanations quickly got me fascinated and I decided to make my 
  own - even more DIY style. I would 3D print everything and make some improvements like bluetooth. Another problem he had with 
  his was the lack of a way to indicate on which layout level you are. And maybe a gesture sensor to swipe with. And maybe a gyroscope 
  to use it like a mouse or driving wheel.&lt;/p&gt;
&lt;p&gt;First I decided to go simple and just make a keyboard without bluetooth or anything fancy. Then learn from the mistakes and improve 
  to make a second one that is closer to the real goal.&lt;/p&gt;
&lt;p&gt;I ordered cheap switches and a pcb to make the process easier. At this point I thought that a big step of this project will be coming 
  up with a good layout for my needs. Also because I take key bindings too seriously.&lt;/p&gt;
&lt;p&gt;I found 3D models from the original designer and decided to print them. I also found a model of a keycap and modified it to make it 
  printable without much support and add a bump for the index fingers. The keycaps turned out not fitting the switches, so I had to 
  rework them a few more times.&lt;/p&gt;
&lt;p&gt;The 3D models can be found &lt;a href=&quot;https://github.com/vs-slavchev/keyboard&quot;&gt;here on my github&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The pcb was not fitting the models I found and was not very happy with them anyway, and they were not designed to be 3D printed 
  to start with. I started planning how could I make a case that does not use bolts and nuts.&lt;/p&gt;
&lt;p&gt;I printed a few simple clips to see if I could use those. Some of them I printed improperly, some were very stiff and I struggled 
  to design better ones. I realised something very important then: instead of going for a perfect solution I should have gone for 
  a satisfactory one and improve when needed.&lt;/p&gt;
&lt;p&gt;I immediately made a few simple holes in a new top half model and found some M3 and M2 12mm bolts. After that I started designing 
  a new bottom case, but adjusting its height would have to wait.&lt;/p&gt;
&lt;p&gt;Another problem was that I damaged or bricked the PCB so I also had to start again with that. The big problem was that I had 
  soldered the switches onto the PCB, so I had to unsolder them one by one. It cost me 4 hours and a few burns to do that. But I 
  did learn to do less ambitious planning upfront. I will never forget this lesson because fixing broken switches is one of the 
  most tedious and fiddly things I’ve ever done.&lt;/p&gt;
&lt;p&gt;The next step was to figure out how to use an Arduino for the keyboard: I could flash it with the QMK firmware, or write some code myself.&lt;/p&gt;
&lt;p&gt;Detecting button presses on 48 keys with fewer than 48 pins is not straightforward. So I found a few excellent blogs 
  on how matrix scanning works:&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;&lt;a href=&quot;https://pcbheaven.com/wikipages/How_Key_Matrices_Works/&quot;&gt;https://pcbheaven.com/wikipages/How_Key_Matrices_Works/&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;https://komar.blog/posts/how-to-make-a-keyboard-the-matrix/&quot;&gt;https://komar.blog/posts/how-to-make-a-keyboard-the-matrix/&lt;/a&gt;&lt;/li&gt;
  &lt;/ul&gt;
&lt;p&gt;After understanding the hardware I did some searching. First I found the matrix scanning code 
  &lt;a href=&quot;https://github.com/Chris--A/Keypad/blob/master/src/Keypad.cpp&quot;&gt;in a file&lt;/a&gt; from the Keypad library for Arduino. Then I found 
  a &lt;a href=&quot;https://github.com/di0ib/StenoFW/blob/master/StenoFW.ino&quot;&gt;file&lt;/a&gt; from a stenography keyboard open source project, which 
  also did a similar job. It was very helpful to learn from this available code.&lt;/p&gt;
&lt;p&gt;Making a keyboard without a pcb is called handwiring and I found a nice post about it: 
  &lt;a href=&quot;https://geekhack.org/index.php?topic=87689.0&quot;&gt;https://geekhack.org/index.php?topic=87689.0&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;With the plan changed, and my student budget limits, I actually had more fun finding my way through.&lt;/p&gt;
&lt;p&gt;Next: &lt;a href=&quot;https://vsslavchev.com/blog/diy-keyboard-key-matrix/&quot;&gt;part 2, scanning the key matrix and fixing ghosting&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title type="html">DnD murder mystery inspiration</title>
    <link rel="alternate" type="text/html" href="https://vsslavchev.com/blog/dnd-murder-mystery/"/>
    <id>https://vsslavchev.com/blog/dnd-murder-mystery/</id>
    <published>2019-05-02T00:00:00Z</published>
    <updated>2019-05-02T00:00:00Z</updated>
    <summary>A murder mystery one-shot for Dungeons and Dragons to borrow from: the pocket dimension setup, the map, the clues and the motive of every suspect.</summary>
    <content type="html">&lt;img loading=&quot;lazy&quot; width=&quot;1282&quot; height=&quot;454&quot; class=&quot;w3-round-large w3-mobile w3-image&quot; src=&quot;https://vsslavchev.com/img/dnd-map-planning.jpg&quot; alt=&quot;DnD murder mystery map planning notes&quot;&gt;
&lt;p&gt;I wanted to make a murder mystery for my DnD group. When the moment came and I started preparing for the session I found there is a lot of 
  advice, but not a lot of easy inspiration. There are a few prepared adventures, and those are not very polished or detailed. I used some of 
  them for inspiration and a base and stole some ideas from them. Maybe you can take mine and improve it to have fun with your players.&lt;/p&gt;
&lt;p&gt;I had planned this for a long time as I expected that one of my players would not attend at some point. Perfect opportunity to throw the 
  characters into a pocket dimension for a one shot without advancing the main story. I gave one of them this book with empty pages and a 
  whirlpool on the cover.&lt;/p&gt;
&lt;p&gt;Make sure to have prepared statblocks for all the stuff they can encounter.&lt;/p&gt;
&lt;p&gt;I was greatly inspired by the adventure called 
  &lt;a href=&quot;https://www.dmsguild.com/product/181052/How-Not-to-Host-a-Murder-5e&quot;&gt;&quot;How not to host a murder mystery&quot;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I have not used names as I think it&#x27;s easier to navigate the story with roles since we are looking at its structure and not for entertainment.&lt;/p&gt;
&lt;p&gt;Everything starts in a whirlpool in the sea, hence the cover of the book. The characters are on a small ship with Victim heading to the
  city on the coast. During the whirlpool another group of adventurers (goblins?), who read another book of the same type, are summoned on 
  the ship. Victim, Innocent and his colleagues are on the ship, and the characters are offered to stay in the Victim&#x27;s mansion.
    They were all summoned because he found this book and wanted to figure it out so cast some spells and activated all the books. Victim
      introduces Innocent as his brave but foolish, quick to act apprentice.&lt;/p&gt;
&lt;p&gt;The mansion, Victim explains was built thanks to his father&#x27;s successful business of trading rapiers, short swords and daggers. His
  father died 6 years ago.&lt;/p&gt;
&lt;p&gt;Garden:&lt;br&gt;
    Morning glory - a flower that blooms in the morning because of sunlight and its petals are closed when in shadow or darkness; a
    letter is hidden under the open petals in the day, but when darkness makes them close it is easy to spot. The letter is written
    with the same handwriting as the book of the Killer, it is to the girl of the apprentice saying that he will not give up on their
      love. This can be used to have the Killer try to frame Innocent.&lt;/p&gt;
&lt;p&gt;Time before dinner: the players are invited to do something before it is time for dinner: &lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;With Killer and Innocent to practice spells [openly said] (so all 3 including the PC seem suspicious); Killer doesn&#x27;t want
    to waste mana; The Innocent calls Killer by his middle-name. The other players will not know whom this name belongs when they see it.&lt;/li&gt;
  &lt;li&gt;With sister to show you around: offers a reward a knife from the father&#x27;s collection of rare craftsman knives [openly said] (so the
    sister and the PC sound suspicious) In private they find that one specific knife is missing from its stand - Lord Kelnor&#x27;s pearl dagger&lt;/li&gt;
  &lt;li&gt;With cook to help prepare the big dinner. [openly said]; In private he tells the motive of both Innocent and Sister. He is grumpy
    and very skilled with cutting meat with his knife; he says the sister has a necklace with a gem that allows her to seduce and control weak men&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The players are given the information separately through notes and they should get a bit confused and suspect each other.&lt;/p&gt;
&lt;p&gt;Dining room:&lt;br&gt;
    During dinner the characters can talk to Innocent, Killer, Victim&#x27;s sister who is a paid assassin, the cook, the old nanny&lt;/p&gt;
&lt;p&gt;Killer says he will go and help the cook to set up the table. Innocent says that he will go and find the host Victim because
  he should have been here already. He leaves the room and in a bit everyone hears him scream.&lt;/p&gt;
&lt;p&gt;Victim is stabbed in the heart in his study. There is no weapon there. Actually the knife Killer used was conjured by Spiritual weapon.&lt;/p&gt;
&lt;p&gt;Killer points the finger at innocent and explains his motive. Innocent explains that he had the choice to give up on his study, but
  made up his mind and was no longer interested in marrying the girl. Innocent has a fresh letter to her stating that he will find a way to
  be with her and won&#x27;t give up on their love.&lt;/p&gt;
&lt;p&gt;The real killer has to be found before sunrise otherwise the framed Innocent will be hanged.&lt;/p&gt;
&lt;p&gt;Innocent is a young novice, brave but foolish, quick to act. Victim doesn&#x27;t let Innocent marry the daughter of a blacksmith because
  she will distract him from his studies.&lt;/p&gt;
&lt;p&gt;Cook - not speaking a lot / low voice / bitter&lt;/p&gt;
&lt;p&gt;The study:&lt;br&gt;
    Has a book with spells (belongs to:Killer&#x27;s name on the inside cover), every page has notes in the margins. Some of the notes are
    crossed out and with another color and handwriting more notes are taken. The second notes match the handwriting on the papers on Victim&#x27;s
    desk. On the spell Spiritual weapon there are a lot of scribbles and in all caps: HAVE TO ASK &amp;lt;Killer&#x27;s middle name&amp;gt; HOW TO CAST THIS!&lt;/p&gt;
&lt;p&gt;Trophies on a shelf saying: First place in spellcasting/conjuring/arcana competition - Victim&lt;/p&gt;
&lt;p&gt;Building the structure of the mystery:&lt;/p&gt;
    &lt;ul&gt;
      &lt;li&gt;Innocent has motive and opportunity(to go bring victim), but no means&lt;/li&gt;
      &lt;li&gt;Killer has motive(fame,praise), means(stabbing), opportunity(the fact that the apprentice and the sister now have a motive)&lt;/li&gt;
      &lt;li&gt;Cook has means(knife with blood), no motive and no opportunity&lt;/li&gt;
      &lt;li&gt;Sister has motive(she is younger and can inherit the mansion that she likes very much if her brother dies), but no means and no 
        opportunity, but definitely suspicious being an assassin&lt;/li&gt;
      &lt;li&gt;Old nanny - no motive, no means, no opportunity&lt;/li&gt;
    &lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title type="html">Switching to and customizing IntelliJ</title>
    <link rel="alternate" type="text/html" href="https://vsslavchev.com/blog/switching-to-intellij/"/>
    <id>https://vsslavchev.com/blog/switching-to-intellij/</id>
    <published>2016-10-18T00:00:00Z</published>
    <updated>2016-10-18T00:00:00Z</updated>
    <summary>Settings and custom keyboard shortcuts that make IntelliJ IDEA comfortable and productive after switching to it.</summary>
    <content type="html">&lt;img loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;246&quot; class=&quot;w3-round-large w3-mobile w3-image&quot; src=&quot;https://vsslavchev.com/img/keybindings.jpg&quot; alt=&quot;IntelliJ keybindings settings&quot;&gt;
&lt;p&gt;Hello! Read on if you decided to switch to IntelliJ, set it up, make it comfortable and learn how to be more productive with it. 
  This post covers some settings and custom shortcuts.&lt;/p&gt;
&lt;p&gt;The first things that annoyed me were the popups in the top-right corner. To disable some of them:
  &#x27;Event log&#x27; on the bottom-right (or Speech bubble icon) -&gt; Settings. Disabling the balloons doesn&#x27;t hide the
  notifications from the event log, so you can check that if you need to.&lt;/p&gt;
&lt;p&gt;Next up is removing the default comment generated at the top of a new class file.&lt;br&gt;
  File -&gt; Settings -&gt; Editor -&gt; File and Code Templates -&gt; &#x27;Includes&#x27; tab -&gt; File header -&gt; the comment is on the right
  -&gt; delete it.
  If you would like to use this feature make sure to print the date in a less ambiguous format.&lt;/p&gt;
&lt;p&gt;Another one is the vertical line in the editor that is set to 120 chars per line by default. To change the line
  limit:
  &lt;br&gt;File -&gt; Settings -&gt; Editor -&gt; Code Style -&gt; Right margin columns.&lt;br&gt;
  &lt;i&gt;The &lt;a href=&quot;https://google.github.io/styleguide/javaguide.html&quot;&gt;Google Java Style Guide&lt;/a&gt; recommends a column limit of 100 chars.&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;Ok hotkeys. Some are very hard to reach, hard to learn or conflict with something else. Here are some recommendations
  how to make your life easier. You don&#x27;t need to remove the old shortcut, so if someone else wants to use your Idea they
  can still use the default keys they know. Go to &lt;br&gt;File -&gt; Settings -&gt; Keymap&lt;br&gt; and find the ones you need to change, then click on
  them and select &#x27;Add keyboard shortcut&#x27;. All of the recommended ones are unused and should be added as alternatives. For each
  entry I have specified: the name of the command, the default shortcut, the suggested one and an explanation.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Rename:&lt;/b&gt; default - Shift F6, suggested - Alt R. This default key is famous for being so hard to reach. Usage of
  rename should be encouraged as naming things is one of the hardest tasks in programming. Remember it as R stands for rename.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Find usage:&lt;/b&gt; default - Alt F7, suggested - rightAlt U. Shows you where a method/variable is used. One hand -
  easy. Associate U with usage.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Surround with...:&lt;/b&gt; default - Ctrl Alt T, suggested - Alt T. Surrounds the selected code with
  if/elseif/while/for/try... etc. Some Linux distros have the terminal shortcut bound to Ctrl Alt T so that causes a conflict. Even if you don&#x27;t 
  care about that the alternative is still 1 less key to press.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Delete line at caret:&lt;/b&gt; default - Ctrl Y, suggested - Alt X. In Eclipse it was easy to Ctrl D with one hand, but
  Ctrl Y requires both hands. The Alt and X keys are next to each other and this one is very easy to press. The X can be associated with
  double-crossing something.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Run:&lt;/b&gt; default - Alt Shift F10, suggested - Alt P. Usefulness depends on how often you run the project. P as in
  play for the green arrow icon. An even easier variant is Alt E as in execute.&lt;/p&gt;
&lt;p&gt;Next, I have compiled a list of the more notable shortcuts. Some are based on people&#x27;s opinion from Stackoverflow. A more complete list 
  can be found &lt;a href=&quot;https://resources.jetbrains.com/storage/products/intellij-idea/docs/IntelliJIDEA_ReferenceCard.pdf&quot;&gt;here&lt;/a&gt;
  and you can always check the key for actions by pressing Ctrl Alt A in Idea.&lt;/p&gt;

&lt;p&gt;Go to declaration =&gt; &lt;b&gt;Ctrl B&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Open class =&gt; &lt;b&gt;Ctrl N&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Rename refactor =&gt; &lt;b&gt;Alt R;&lt;/b&gt; Shift F6&lt;/p&gt;
&lt;p&gt;Refactor variable from expression =&gt; &lt;b&gt;Ctrl Alt V&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Extract method =&gt; &lt;b&gt;Ctrl Alt M&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Smart completion =&gt; &lt;b&gt;Ctrl Shift Space&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Cyclic expand word =&gt; &lt;b&gt;Alt forwardslash&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Complete statement =&gt; &lt;b&gt;Ctrl Shift Enter&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Reformat, Checkstyle, fix imports =&gt; &lt;b&gt;Ctrl Alt L&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Find in whole project =&gt; &lt;b&gt;Ctrl Shift F&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Switch tabs =&gt; &lt;b&gt;Ctrl Tab&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Quick fix/intentions =&gt; &lt;b&gt;Alt Enter&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Smart template =&gt; &lt;b&gt;iter/psf/thr Tab&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Find Usage =&gt; &lt;b&gt;rightAlt U;&lt;/b&gt; Alt F7&lt;/p&gt;
&lt;p&gt;Brief info =&gt; &lt;b&gt;Ctrl mouseover&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Duplicate =&gt; &lt;b&gt;Ctrl D&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;New line below =&gt; &lt;b&gt;Shift Enter&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;New line above =&gt; &lt;b&gt;Ctrl Alt Enter&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Expanding select =&gt; &lt;b&gt;Ctrl W&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Delete to word start =&gt; &lt;b&gt;Ctrl Backspace&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Delete line =&gt; &lt;b&gt;Alt X;&lt;/b&gt; Ctrl Y&lt;/p&gt;
&lt;p&gt;Paste previous copies =&gt; &lt;b&gt;Ctrl Shift V&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;JavaDoc =&gt; &lt;b&gt;Ctrl Q&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Run =&gt; &lt;b&gt;Alt P;&lt;/b&gt; Alt Shift F10&lt;/p&gt;
&lt;p&gt;Go to previous/next method =&gt; &lt;b&gt;Alt Up/Down&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Go back to code =&gt; &lt;b&gt;Ctrl Alt Left&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Go to next error =&gt; &lt;b&gt;F2&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Expand all folded =&gt; Ctrl Shift =&lt;/p&gt;
&lt;p&gt;Parameter info in method call =&gt; &lt;b&gt;Ctrl P&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Show TODO list =&gt; &lt;b&gt;Alt 6&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Bring up the file/class/method quicksearch =&gt; &lt;b&gt;Shift Shift&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Open the switcher =&gt; &lt;b&gt;Ctrl Tab&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Hide active window =&gt; &lt;b&gt;Shift Escape&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;If you think a shortcut or setting you often use is missing - tell me!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title type="html">The mystery of termite communication</title>
    <link rel="alternate" type="text/html" href="https://vsslavchev.com/blog/termites/"/>
    <id>https://vsslavchev.com/blog/termites/</id>
    <published>2015-11-06T00:00:00Z</published>
    <updated>2015-11-06T00:00:00Z</updated>
    <summary>How termites gather wood into piles by following simple rules that add up to complex emergent behaviour, and my simulation of it.</summary>
    <content type="html">&lt;img loading=&quot;lazy&quot; width=&quot;665&quot; height=&quot;81&quot; class=&quot;w3-round-large w3-mobile w3-image&quot; src=&quot;https://vsslavchev.com/img/artificial.jpg&quot; alt=&quot;Termite simulation screenshot&quot;&gt;
&lt;p&gt;Until the 1960s scientists could not explain how termites manage to organise their activity to gather and pile up
  in one place all the wood pieces they find. Different theories tried to explain this like inaudible languages, but
  the truth was that they just followed simple rules, from which emerged a complex behaviour.&lt;/p&gt;
&lt;p&gt;By taking a piece of wood when they encounter it and dropping it when they encounter another one the termites
  move the pieces to one or more piles, which in turn also get combined into bigger piles. The result is cleared
  areas and compact piles.&lt;/p&gt;
&lt;p&gt;My implementation of the simulation: &lt;a
    href=&quot;https://github.com/vs-slavchev/Small_projects/tree/master/TermiteSimulation&quot;&gt;on github&lt;/a&gt;&lt;/p&gt;
</content>
  </entry>
</feed>
