• As an active scout leader, I know the struggle: You’re planning a summer camp for 80 participants, you have 15 different tents (traditional German “Kohten”, yurts, large group tents), and somehow you need to figure out how it all fits on the meadow. We used to do this with paper, pencil, and a lot of hope. Now there’s an app for that.

    Note: The app is in German since it’s built for German-speaking scout groups, but the concepts apply universally. If you’re interested I’m happy to update it to english too.

    The Problem: Planning a Camp is Tetris on Hard Mode

    Anyone who’s organized a scout camp knows these challenges:

    1. Tents Aren’t Simple Squares
    A “Kohte” (traditional German scout tent) is 3 meters in diameter. But with guy ropes, you need a clear circle of 8 meters. A yurt? 5 meter tent, 11 meter total footprint. And a rectangular group tent like the “Lokomotive”? 4×8 meter tent, but the guy rope distance on the sides differs from front and back.

    2. Spacing Matters
    You need walking paths between tents. Fire pits need distance from sleeping areas. The kitchen shouldn’t be right next to the sleeping tents (morning cooking smells at 6 AM…).

    3. Different Suppliers, Different Dimensions
    Jurtenland, Tortuga, Lanco – each manufacturer has their own tent types with specific measurements. Who has all those data sheets memorized?

    4. The Field Always Looks Different
    The plan on paper looked great. But on the actual meadow? No idea if it fits. And if it doesn’t, you improvise – with questionable results.

    5. Planning Happens in Meetings, Execution Happens On-Site
    You spend hours in leader meetings (“Leiterrunde”) discussing the perfect layout. But when you actually arrive at the campsite with 50 kids and a truck full of tents? You need to act fast. The paper plan is in someone’s backpack, the meadow looks different than expected, and there’s no time to recalculate everything from scratch. You need a tool that works on your phone, right there on the field.

    The Idea: Google Maps Meets Tent Catalog

    My solution: A web app that displays tents to scale on a real map.

    The concept:

    • Find your campsite on the map (satellite view!)
    • Drag tents from a catalog onto the meadow
    • The app shows the tent AND the guy rope area to scale
    • Rotate, move, plan – all in real-time
    • Save and share the plan with your team

    The Tech Stack: Modern but Pragmatic

    For a evening project, I wanted fast results:

    • React + TypeScript – Type safety saves debugging time
    • Vite – Fast dev setup, no Webpack config drama
    • Tailwind CSS – Styling without CSS file chaos
    • Leaflet + React-Leaflet – Maps with full control
    • Supabase – Backend as a Service (Auth, Database, free for side projects)

    Why Leaflet instead of Google Maps?

    • Free (Google charges money after certain traffic)
    • Multiple map layers (OpenStreetMap, Satellite, Topographic)
    • More control over custom markers

    Core Features

    1. Tent Catalog with Real Dimensions

    I’ve added the most common German scout tents with their actual measurements:

    • Kohte (3m Ø, 2.5m guy rope radius)
    • Yurts in 5m, 6m, 8m, 10m
    • Tortuga tents (Lokomotive, Berliner, Sahara)
    • Kitchen tents, storage tents

    Each tent has: footprint, guy rope radius (for round tents) or separate guy rope distances for width/length (for rectangular tents), capacity, and supplier link.

    2. True-to-Scale Rendering

    This was the trickiest technical part. At zoom level 19, one meter on the map is about 0.3 pixels. At zoom level 21, it’s 1.2 pixels. The tents need to scale correctly at every zoom level.

    function getPixelsPerMeter(lat: number, zoom: number): number {
    const earthCircumference = 40075016.686;
    const metersPerPixelAtZoom0 = earthCircumference / 256;
    const metersPerPixel = metersPerPixelAtZoom0 *
    Math.cos((lat * Math.PI) / 180) / Math.pow(2, zoom);
    return 1 / metersPerPixel;
    }

    3. Rotation for Rectangular Tents

    Round tents you can just place anywhere. But a 4×8 meter group tent? You need to rotate it. The slider in the sidebar rotates the tent in real-time on the map – including the correctly rotated guy rope area.

    4. Distance Measurement

    One click on “Show Distances” displays the spacing between all tents. Red = too close (guy rope areas overlap), Green = OK.

    5. Offline-First with Cloud Sync

    Projects are stored locally in the browser first. No account needed to try it out. If you want to share your plan, you can sign up and migrate the project to the cloud.

    6. Share via Link

    After migration, you get a share link. The team can view the plan – no account required.

    What I Learned

    1. Supabase is Amazing
    Auth, database, row-level security, realtime – all out of the box. Perfect for side projects.

    2. Leaflet > Google Maps (for this use case)
    More control, free, custom markers without limits.

    3. Offline-First Makes Everything More Complex
    Local IDs vs. cloud UUIDs, sync conflicts, migration – if I’d thought about this more upfront, the code would be cleaner.

    4. Domain Knowledge is Essential
    Without my scouting experience, I’d never have known that guy rope distances on rectangular tents can differ on each side.

    What’s Next?

    The project is live and usable. Planned features:

    • Participant Management: Who sleeps where?
    • Material Checklist: What needs to come along?
    • PDF Export: Print the plan for on-site use
    • More Campsites: Database of known scout campsites
    An overview of a campsite layout, showing various tent locations marked with icons, including a campfire symbol and different tent sizes listed alongside.
    You can preview one of the camps: https://pfadfinder-zeltplaner.vercel.app/p/e12be65d648719cbf8e04c2ec97a9bfe

    Conclusion

    A problem to solve turned into a tool that’s actually useful. The beauty of side projects: You solve real problems you personally understand.

    If you’re planning scout camps too give it a try: https://pfadfinder-zeltplaner.vercel.app/

    Questions about the tech stack or implementation? Let me know!

    PS: Yes, I used AI (Claude) for vibe programming. Coding with an AI is like having a colleague who never gets tired and knows every library. But the ideas and domain knowledge – that’s still on you.

  • Daily writing prompt
    What could you do differently?

    The question is perfectly timed for the new year. I started the year 2026 with a few resolutions and most of them are about doing things differently.

    The main resolution I want to approach differently this year is how I plan my meals and what I eat. I want to eat less overall and be more intentional about every meal. As a vegetarian, I’m focusing on choosing fresh, whole foods over processed options. I also want to pay attention to portion sizes so I don’t overeat. Overall, I hope these changes will help me feel healthier and more energized.

  • I didn’t like today’s prompt, so I went ahead and dug into the archive and selected this one:

    How have your political views changed over time?

    As long as I remember or cared about, my political views haven’t really changed a lot. From being left-centric to becoming more and more leftist over the years. My main focus point meanwhile shifted to social justice and fairness.

  • Do you play in your daily life? What says “playtime” to you?

    As my kiddo grew up, video games turned into a regular thing for him. He keeps asking for games like Fortnite, Roblox, and GTA V, but we’re trying to keep those in check for now. Nowadays, every game seems to have online multiplayer! To help him ease into it, my wife and I jumped into Minecraft with him. Luckily, Microsoft has this cool Realm feature, so we can all play together and still have our own fun in the same world.
    My go-to gaming gear is all over the place—I’ve got handhelds, consoles, VR headsets, and a gaming PC in the mix.

  • What are your biggest challenges?

    Carrying responsibility without clear ownership has been a recurring theme in my life, both personally and professionally. I’ve often encountered gaps in responsibilities, and I step up to ensure that tasks are completed efficiently. While this approach typically yields positive results, it often demands a significant amount of energy and involves considerable context switching. It’s not uncommon for friends and colleagues to approach me for guidance on projects in which I have minimal involvement. Thanks to my talent for improvisation, we usually arrive at effective solutions; however, I believe that leveraging the expertise of the individuals who truly own these projects would lead to even better outcomes.

    One way I’m trying to improve is by turning my instinct to help into a habit of clarification. When someone pulls me into a problem I don’t own, I now try to respond with questions before answers: What decision needs to be made? Who is accountable for it? What constraints already exist? This often surfaces that the project already has an owner who simply hasn’t been empowered or challenged yet. By staying in a coaching role rather than an execution role, I conserve energy, reduce context switching, and create space for better expertise to lead the solution.

  • What makes you feel nostalgic?

    Playing old game titles or their newer releases. Just recently, I added Gran Turismo 7 to my PlayStation library, and it immediately threw me back to the old times playing Gran Turismo on my PlayStation 1 on a CRT screen in my room together with my dad.

    Those were special times for me as my dad really enjoyed spending time with me this way, and we had a great chat during those games. Now, almost 30 years later, I brought my old PS4 to his place and made sure he could play GT7 on it in the hopes of making him remember the good old days too.

  • This is a new series I wanted to try based on the daily suggestions given in the Jetpack App in my phone. My goal is to get better in blogging and keeping it recurring!

    Which person had the biggest impact on your life?

    2025-12-30

    While answering this with “my parents,” “my wife,” or “my first boss” might sound easy, I wanted to use this prompt to think deeply about a specific kind of person who really influenced my life.

    My teachers

    Last week I attended a “pre-Christmas” party in my hometown and met two of my former teachers there. While reflecting on them, I admitted to myself that they are two of the very few teachers who had an everlasting impression on me. They not only did their job 9-5, but they really cared about the things they aimed to teach.

    Both of them offered after-school activities centered around the outdoors, nature, and bees. What I learned there was super impactful to me and was way more than you usually learn.

  • Yesterday I posted about my journey to automate the lights in my office room and only a few hours later, thanks to rubber ducking while writing the post and messages I received after publishing, I had a solution working that does exactly what I wanted to do. So this is now the final flow:

    Turning Lights On

    Once a motion is detected in the room it runs the following script to set the current timestamp as a variable:

    global.set('LastTriggerTime', Date.now());

    Using global.set() allows me to store this LastTriggerTime for a longer period and makes it accessible for other flows.

    After setting the timestamp, the desk light and underdesk light turn on. If the luminance of the room is below 25, the ceiling lamp turns on as well.

    Turning Lights Off

    No more variables and wait times! Just a single script:

    const lastTriggerTime = global.get('LastTriggerTime');
    const tenMinutesAgo = Date.now() - (10 * 60 * 1000);
    if (lastTriggerTime > tenMinutesAgo) {
      return true;
    } else {
      return false;
    }
    

    What the Code Does

    • If not, it returns false and the next logic block turns off all the lights.
    • global.get('LastTriggerTime') retrieves the last stored motion timestamp.
    • Date.now() gets the current time in milliseconds.
    • tenMinutesAgo is calculated by subtracting 10 minutes (in milliseconds) from the current time.
    • If the last motion was more recent than 10 minutes ago, the script returns true and the lights stay on.
    • If not, it returns false and the next logic block turns off all the lights.

    The final flow uses a simple logic comparator on this result. If the script returns false, meaning no motion in 10 minutes, the flow powers off all lights. Clean and reliable.

  • I’ve been using Homey for quite a while now. While it doesn’t offer all the flexibility and customization options that some other platforms might, it provides an excellent way to create flows and centrally manage all your smart home devices, even across multiple vendors and standards.

    Recently, I added a new device to my office: a smart ceiling light from Govee. My plan was to automate the lighting in my office to make it, well, a bit more nerdy. The idea was (and still is) simple:

    I placed a motion sensor (in this case, an IKEA VALLHORN) in the room. The goal: when motion is detected, turn the light on. When no motion is detected, turn the light off.

    What can I say? It wasn’t that easy.

    Unfortunately, sitting at my desk triggered frequent “no motion” events for the IKEA sensor, causing the light to turn on and off during work sessions. This was especially annoying during calls when I sat still for long periods.

    My idea of the perfect setup

    The Delay

    Next Idea: Add a delay and a check again if the motion alarm is still off after 5 mins:

    Can you guess what happened? Yes, each time the sensor’s motion alarm turned off, it waited five minutes and then checked again. If I happened to move during that exact time window, it worked. But solving this by adding multiple checks (one after four minute, another after one minutes) didn’t help. Homey runs all those actions in parallel. So even if I moved in one of the flows, another one that started 30 seconds later might still turn off the light.

    The Variable

    To prevent multiple flows to run at the same time and do the same thing, I added another idea: The Variable!

    Now, whenever a test starts, the flow first checks if a variable called TestInProgress is set to true. If it is, the flow just ends. If not, it sets the variable to true and enters the first delay. After each delay, it checks again for motion. If motion is detected, the variable is reset to false and the flow ends. If I don’t move, or if I’ve left the room, the light turns off and the variable is reset to false as well.

    The (Not-Yet-)End Result

    What I’ve ended up with is something much more complex than initially expected: five delays, each shorter than the last, with checks that allow the script to exit early if motion is detected.

    The next step will be to use Homey’s JavaScript functionality to simplify things (at least in my head it sounds simpler):

    • Add a new variable LastTriggerTime that records the last time motion was detected.
    • Once the flow is triggered due to no motion, check if another instance is already running. If not, wait a few minutes.
    • After waiting, only turn off the light if LastTriggerTime is older than the current time minus ten minutes.

    Let’s see how that goes!

  • This is again a post about the scout project I am supporting. This time it is about the way how the scout groups can counter the CO2, that was or will be emitted during the project timeline. The ask to me was “easy”:

    We want to display three things:

    1. The foot-print of the project (Co2 you emit during the timeframe that can be directly attached to the project)
    2. The hand-print of activities (The groups do small scale projects that save a Carbon Dioxid Equivalent value)
    3. And a tree that is animated and shows the progress of the hand-print catching up with the foot-print.

    The scout groups should have the possibilities to submit their projects through a simple form on the website and once we approved the project it should automatically calculate towards the hand-print. The foot-print will be pre-calculated by us and updated on the fly.

    To make this happen, I decided to use custom post type for the submitted projects. This allowed each scout group to easily submit their activity through a user-friendly form, while giving us the flexibility to moderate and approve projects before they were counted in the statistics.

    Custom Post Types and Meta Fields

    Each project submission is stored as a custom post type called “Projekte.” To keep track of the CO₂ savings for each activity, I added a custom meta field (let’s call it co2_ausstoss) where the group can enter the amount of CO₂e (in kilograms) their project saved or offset. This field is included in the submission form and is required for approval.

    Calculating the Totals

    To display the total hand-print (sum of all approved project CO₂ savings), I wrote a small PHP function that queries all approved “Projekte” posts, sums up the co2_ausstoss values, and converts the total from kilograms to tonnes. This value is then made available via a WordPress shortcode, which can be placed anywhere on the site.

    The foot-print is managed by us as a simple number in the backend and updated whenever needed. This keeps the process transparent and under control, while the hand-print grows dynamically as more projects are approved.

    How I used the shortcode in the DOM (hidden):
    Since I needed to use the calculated value in JavaScript, I placed the shortcode inside a hidden <div> with a data attribute. This way, the value is available for scripts but not visible to users.

    <div id="co2data" data-co2-sum="[hand_print_co2]" data-co2-max="[foot_print_co2]" style="display:none;"></div>

    When the page is rendered, WordPress replaces [hand_print_co2] with the actual value of the hand-print, e.g. 12,34 and the [foot_print_co2] with the foot-print value.

    Animating the Tree

    One of the most engaging parts of the page is the animated tree. The idea was to visually represent the progress of the hand-print catching up with the foot-print. For this, I used two overlapping divs, both with the same tree image as the background. The bottom layer is greyed out using a CSS grayscale filter, while the top layer remains in full color.

    The colored tree “grows” from the bottom up: as the hand-print increases, the height of the colored div increases proportionally, revealing more of the vibrant tree. This is handled with a bit of JavaScript that reads the current hand-print value, calculates the percentage compared to the foot-print, and adjusts the height of the colored div accordingly. I also experimented with a dashed line at the top of the colored area to show the current progress, but found that a simple growing tree was the clearest visual indicator.

    Example:
    Here’s the HTML structure for the tree:

    <div id="tree-container">
    <div id="tree-background"></div>
    <div id="tree-progress"></div>
    </div>

    And the CSS:

    #tree-container {
    position: relative;
    width: 278px;
    height: 300px;
    overflow: hidden;
    margin: 0 auto;
    }
    #tree-background {
    position: absolute;
    bottom: 0;
    width: 100%;
    height: 100%;
    background-image: url('tree-url');
    background-position: bottom center;
    background-repeat: no-repeat;
    filter: grayscale(100%);
    }
    #tree-progress {
    position: absolute;
    bottom: 0;
    width: 100%;
    height: 0;
    background-image: url('tree-url');
    background-position: bottom center;
    background-repeat: no-repeat;
    transition: height 1s ease-in-out;
    }

    And the JavaScript that makes the tree grow:

    document.addEventListener("DOMContentLoaded", function () {
    const co2Element = document.getElementById('co2data');
    const co2Value = parseFloat(co2Element.dataset.co2Sum.replace(',', '.')) || 0;
    const maxHeight = 300; // px
    const co2max = parseFloat(co2Element.dataset.co2max.replace(',', '.')) || 0;
    const height = Math.min(Math.ceil((co2Value / co2max) * maxHeight), maxHeight);

    const treeProgress = document.getElementById('tree-progress');
    if (treeProgress) {
    treeProgress.style.height = `${height}px`;
    }
    });

    The Result

    Now, as scout groups submit and complete their projects, the tree on the website visibly grows, showing in real time how collective action is helping to offset the project’s carbon footprint. It’s a fun and motivating way to make progress visible to everyone involved-and a great reminder that every small action counts!

    If you’re interested in the technical details or want to implement something similar for your own project, feel free to reach out or check out the code snippets. You can checkout the tree at crover.info.