Sound mixer
Your desk, your atmosphere. Spotify has its own player controls.
Sound is off. Always your choice.
Your desk, your atmosphere. Spotify has its own player controls.
Sound is off. Always your choice.
Loading Spotify…
Spotify stays in stereo in the room. For the 3D headphone effect, choose a local music file in Sound mixer. Spotify may offer previews depending on your session.
PETTERI HELTTULA · PRODUCT DESIGN & AI DESIGN ENGINEERING
I turn messy product problems into clear, useful things that work.
Product design, UX, interaction design, AI design engineering, and validation.
Finland · Available for thoughtful product work.
Peluutin · KorisLab · Helsinki Sports Map · Brio · S-hävikki · Tahti
This portfolio is the living version of my resume: open the projects to see the decisions behind the work.
I've been interested in people for much longer than I've been interested in design. Why people behave the way they do, what influences their decisions, and why we sometimes act against our own intentions have fascinated me since I was a kid.
Product design became a practical outlet for that curiosity. Today, I'm most interested in where human behavior, business, technology and data meet.
Behind every click, purchase, abandoned flow and metric is still a person. That's why behavioral science and psychology influence how I think about products.
I also try not to confuse an observation with its explanation.
A million visits doesn't automatically mean success. Nobody using a feature doesn't automatically make it useless. People saying they want something doesn't mean they'll actually use it.
Data gives us signals. The interesting part is figuring out what could explain them — and how confident we should be in that explanation.
When something is cheap to build and easy to reverse, a working product can sometimes answer a question better than asking people to imagine using it.
I don't build because I assume an idea is good. Sometimes I build precisely because I don't know if it is.
That doesn't make usage data the truth either. Low adoption could mean low value, but it could also be discoverability, UX, implementation, context or simply insufficient evidence.
Different questions need different methods. Interviews, research, usability testing, analytics and working experiments are tools for learning — not rituals I need to perform in a particular order.
A product has to create value for the people using it and for the business sustaining it.
I care about conversion, retention, growth and revenue, but also about what we're optimizing and what behavior those decisions encourage. The highest-converting solution isn't automatically the best long-term product decision.
The interesting problems are often the ones where user and business incentives can be made to point in the same direction.
I've worked in teams and understand why shared planning, documentation and coordination matter.
But a team of twenty, a team of three and a team of one don't need the same machinery.
I scale process with the people, risk and complexity involved. The same applies to tools: I move between design, code, AI, research and data based on what helps me answer the question.
Tools change. The goal doesn't: understand the problem well enough to make a better decision.
— Petteri
Your notes stay in this browser on this device.
Black to move. Find the one move that ends the game.
Black to move. Find checkmate in one.
Most of my projects start as a question I can’t let go of. Agents help me turn it into something people can try.
About meHelsinkiProduct designer & AI design engineer. ICT engineering background, curious about everything.

Basketball analytics for Finland’s Korisliiga: teams, players and matchups.

Playing time, substitutions and animated training exercises for junior coaches.

Every sports venue in Helsinki, with filters and routes including HSL transit.

Limit selected apps, then complete a chosen activity to keep scrolling.

A retail service concept for reducing food waste and near-expiry checks.
TahtiA Chrome extension that presents selected text one word at a time.
Sports venues across Helsinki, and how to get to them.
A mobile-focused sports map covering all of Helsinki, bringing venue discovery and route planning into one place for residents and visitors.
Outcome
Find a sports venue in Helsinki and plan the trip there in one map, instead of searching for the place and then the route in another app.
Venues across the whole city, with walking, cycling, driving and HSL routes to each. Prices and accessibility details still need verification.
Marked text appears only in the full case

Finding a sport can mean several searches and a visit to each venue’s website for practical details.
I designed and built the discovery flow and integrations, changed the map implementation when optimisation consumed too much time, and created a queue for manually checking venue data.
The map covers Helsinki. Source data can confirm free use; paid access, prices and venue accessibility still need verification. The interface still needs refinement: I wanted the whole journey to work first.
The idea came from the effort of finding somewhere to exercise: scattered search results, incomplete information and more searching on venue websites. I wanted residents and visitors to find an activity and work out how to get there in one place.
I prioritised mobile because I expect people to use the map while choosing a destination or moving around Helsinki. Sport filters, address search, optional current location and venue details form the discovery flow. A linked text-based list can be used by keyboard and screen reader, so venues remain browsable without selecting map markers.
The list first opened as a drawer over a full-width map on every screen size. On desktop I made it a fixed column beside the map, so browsing venues and seeing where they are happen at the same time; phones keep the drawer.
I first built a custom renderer for a distinctive visual experience. As the dataset grew, responsive zooming and rendering became a substantial development task. Spatial chunks, background rendering in a worker and reused frames helped, but optimisation continued to consume time.


I switched to MapLibre GL JS with OpenFreeMap’s Positron vector basemap, using its touch gestures, clustering and 2D or tilted views.
The switch gave up some of that character in exchange for the whole city, smooth zooming and time for the product itself.
My priority: put development time into helping people find a suitable place and get there. Maintaining the custom visual engine was taking too much time from that goal.
Walking, cycling and driving routes sit alongside public transport through HSL’s Digitransit routing API. Helsinki’s Paikkatietohaku provides address lookup through separately requested API access; the city’s WFS street index supports address suggestions.


Venue data comes from LIPAS, enriched with Helsinki Service Map information. These sources provide citywide coverage, but completeness varies by field. Free use can be confirmed from available data; paid access and exact prices need further checking.
I wanted people to make a practical decision from the information shown. A missing price could not be treated as free access, so I built a manual review queue. It separates incoming venue data from the information I am prepared to approve.
Each candidate gets a rule-based evidence score rather than a probability. A recognised price format, a match to the LIPAS venue and a fresh source add points, while an unfinished cross-source check holds its points back. The review tool only changes local review state: it cannot write to the public dataset or trigger syncs.

Price conditions matter too: an adult ticket, a concession and an equipment charge represent different costs. Current work focuses on making those details and venue accessibility information available, so people can judge suitability before travelling.
Agents mapped the open data sources and their terms (LIPAS, Helsinki Service Map, Digitransit) and built the integrations. I designed the discovery flow for a visitor new to Helsinki, decided when to drop the custom map, and review venue data through a manual queue.
The interface still needs tweaking. I wanted the whole journey, from finding a venue to getting there, to work the way I intended before polishing the details.
City-specific configuration and data providers are separated from the interface and renderer. That makes expansion a matter of adapting sources and configuration rather than rebuilding the application.
A Chinese-speaking friend has helped with translations toward Chinese-language support for visitors. Longer term, I want a trending layer based on recent interest. That needs enough real activity and a clear measurement window; the current preview is illustrative.
Explore Finnish basketball through questions, teams and games.
I designed and built an analysis tool for the men’s and women’s Korisliiga, connecting season statistics, player profiles and match analysis. Search lets people begin with a team, a player or a supported question about a season.
Outcome
Korisliiga games, players and teams compared in ways a box score can’t show.
Shot maps, game progression and on/off-court comparisons for both leagues from 2024–25 onward, with the number of games beside every comparison.
Marked text appears only in the full case
I wanted to compare teams and players across games, and understand the context behind their numbers. I could not find the comparison experience I wanted for Finnish basketball.
I designed and built the analysis experience, making questions a route into the data and showing the context needed to interpret comparisons. I guide the coding agent’s implementation, review the working product and track upcoming changes as Linear tickets written by an agent.
KorisLab covers the men’s and women’s Korisliiga from 2024–25 onward, mainly using Basket.fi data. Coverage varies by tool; ongoing work includes interface improvements and data validation.
I enjoy basketball and comparing teams and players. A single box score felt insufficient as my interest in analytics grew, and I could not find the comparison experience I wanted for Finnish basketball. I built KorisLab to connect season overviews, profiles, match statistics and analysis.
It started as KorisIQ and covered only the women’s Korisliiga. Once that league worked end to end, I wanted more data and added the men’s league.
I want Finnish basketball to receive more attention. The depth of NBA analysis inspired me to explore a smaller-scale version for Korisliiga, with fans, commentators, coaches and players in mind.
Search finds teams and players, and answers supported season questions with a route into the relevant profile. People can start with what they want to know, without first learning which view contains the answer. Asking who had the most assists in the 2025–26 season returns player and team rankings, with Jeanae Terry leading the players on 213 assists, tied to the selected season and verified games.

The answer gives users a starting point and a next step into the underlying detail. I want to expand the range of questions over time, returning an answer, opening a relevant view or combining both.
Matchup Lab compares teams and players across opponents, seasons and recent form: team versus team, the same team across seasons, the last five games versus earlier games, whole games with a player present or absent, and periods with a player on or off court; for players, player versus player, seasons, recent form, and the regular season versus playoffs. Game counts, units and league ranks stay visible so differences can be read in context.
The team-versus-team example below uses a radar chart alongside the original statistics. The chart offers a quick visual comparison, while the table makes the values and their meaning available for closer reading.
My design priority: keep the sample, units and chart scale visible beside the comparison.

The profiles describe the available games. A larger radar area does not establish why a team performed differently, and a small sample needs further context before drawing conclusions.
Game pages now replay a game event by event: real shot locations, the running score, player lines and a win probability curve as the timeline, with jumps to the turning points.

The replay shows only what the data knows. Shots sit where they were taken; other events appear on the half of the court where they happened, because there is no player tracking. The win probability model was tuned on 2024–25, locked and tested once on 2025–26. It was too confident towards the home team in the men’s league, and the game page says that below the chart.
I wanted NBA 2K-style ratings that tell at a glance what a player is good at. The real question wasn’t which number to show, but what a number lets you conclude.
Six areas, no overall score. A three-point shooter and a post player can be equally valuable in completely different ways, so the card rates scoring, shooting efficiency, threes, playmaking, rebounding, and steals and blocks on a 1–99 scale. There’s no 100, because the data never knows everything. Every rating sits on its real statistic: not just “98”, but “98 · 32.3 points per 40 min”.
Short minutes don’t make a star. Per-40 figures flatter small samples: 7 points in 5 minutes works out at 56 points per 40, without knowing whether that pace would last against starters or with more responsibility. Ratings appear only for regular players, small samples are pulled towards a typical regular, and the card shows the minutes and a confidence level. The pull grows the shorter a player’s minutes are, up to three times as strong, with 28 minutes a game counting as full minutes. My first version pulled towards the league’s minute-weighted average, which starters dominate, so even a one-minute player rated about 60. It now pulls towards the median regular.


Distance, not just rank. The first version rated players by rank, which flattened the top: De’Vion Harmon’s 36.3 and Damian Dunn’s 29.5 points per 40, more than 20% apart, became 98 and 96, and a 97 meant something different each season. Ratings now use a fixed scale built from past seasons’ regulars and measure distance from a typical level, with the rank shown separately. On this scale, 20 rating points equal one standard deviation, so about one regular in 40 reaches 90 in a single area.
Say what the data can’t see. The box score only records steals and blocks, so that area is called exactly that rather than “Defence”, and the card says it can’t see on-ball defending or positioning. Player types come from a few readable rules, such as a three-point rating of 70 or more for a shooter. “Versatile” went to too many players at first; it now needs 60 or more in five of the six areas.
A number is published only with its context, and uncertainty is shown rather than hidden.
Page-specific metadata, canonical URLs and a sitemap help search engines and AI search find league, season, player and game pages. Pre-rendered summaries make the content readable before JavaScript runs.
Player and verified-game pages carry their statistics and source links in the HTML itself, with Schema.org data, so a search result can show real numbers rather than an empty app shell.
Page titles use the words people search for, with the league and season in each. The sitemap is submitted in Google Search Console, Google has indexed the site, and it is linked from this portfolio and its public GitHub repository. Search engines always get the Finnish version, because one address can only have one indexable language; an English version would need its own addresses.
Being indexed is only the first step. Next I want to see which searches lead to the site, and whether AI answers cite it at all; I haven’t measured that yet.
As KorisLab grew, the first interface felt too much like a generic AI-generated dashboard: competing card colours and highlights made the analysis harder to read. I refined the identity and hierarchy so the product’s depth was easier to see. Green carries the brand and orange guides data comparisons. Headline figures, units and supporting context have clearer roles, and recent-game values, season baselines and shared chart axes make comparisons readable.
I connected the home page more closely to the product. Dot-matrix figures and dither give it character, while the dashboard preview uses the same working overview component as the application, with hover details and links into data views.
I also adjusted spacing and card density so more information fits on screen at a glance. Desktop and mobile layouts were refined separately to keep related figures together and make browsing more comfortable with less scrolling.
Real player photos looked good, but they carried rights belonging to photographers, players and clubs. I removed them and drew dot-matrix figures in code instead.
I created KorisLab’s first design system in Paper and code to make collaboration with a coding agent easier. Design tokens and reusable components in Paper have corresponding tokens and components in the application code.
I chose Paper over Figma because the more generous MCP call allowances on Paper’s free and Pro plans give me more room for frequent design-to-code iterations. Through Paper MCP, the agent can inspect my designs and use them as a reference for implementation.

Work in progress
Sim Studio plays an upcoming Korisliiga game through 1,000 times and shows what those runs add up to: the win probability, the spread of the points margin and the chance of overtime, how wins usually come about, and each player’s likely points, passing pairs and one simulated game to watch on a 3D court.

Basket.fi tells you what happened. Sim Studio shows how an upcoming game is likely to go, not just who wins. It also surfaces patterns a statistics page can’t: in Korisliiga, a possession that starts from a steal produces about 20 more points per 100 possessions than one after the opponent scores. That describes the season; it isn’t a forecast.
The source is Basket.fi’s official play-by-play: every shot with its location, pass, rebound, turnover and substitution, from about 600 games over three seasons in both leagues. A rebuilt game is accepted only if its points match the official box score; otherwise it is left out and the reason is logged, so data quality is checked before the model, not after. Each game is then split into possessions, about 80 per team, each with who was on court and how it started and ended.
Player profiles come from that data: how often a player acts, where they shoot from and how well, and who they pass to. Small samples are pulled towards the league level, so a player who has made 5 of 6 threes isn’t treated as an 83% shooter until more evidence builds up. The simulator is written in Python, the interface in React with a three.js court, and no paid AI services are involved.
Take one possession: Kauhajoki wins the ball from a steal. The model picks who acts from the players on court by how much each usually has the ball, draws a shot location from that player’s own shots, and settles the shot from their accuracy, the opponent’s defence and the fact that possessions after a steal are more efficient in the league. Played possession by possession against the clock, one run is one possible night. The spread of 1,000 runs is what the model actually claims.
It can tell how likely each result is, whether a game is likely to be close or one-sided and how often it is decided in the last quarter, and who scores, who passes to whom and how widely a player’s night may vary. The percentages are measured, not guaranteed: in the men’s test they sat too close to 50%.
It can’t tell the winner for certain, why a team wins, anything about absences or tactics, which aren’t in the data, or how players really move. Movement on the 3D court is an illustration, not tracking data.
Every game of the men’s 2025–26 season was forecast using only the data before it, so the model never saw the future. Its settings were chosen on the previous season and locked before the test, so the result couldn’t be tuned to look good. The Brier score measures the squared error of a probability: 0.25 is a coin flip, and lower is better.
| Model | Brier score |
|---|---|
| Coin flip | 0.250 |
| Elo rating | 0.2386 |
| Sim Studio | 0.2382 |
| Simple points-margin model | 0.2329 |
The more complex model didn’t beat the simple one, and the result is reported as it is. When it gave the home team 60–80%, the average forecast was 67% and home teams won 71% of those games. That gap points to the model’s main weakness: it ranks games better than the margin model, but its percentages are too cautious. Predicted and actual player points correlate at 0.65, with an average error of about 4.6 points. The project started from a forecast that was right in 60% of 12 games; the locked test turned that into a measured result.
The women’s model got the same test: 83 games, 0.210 against 0.195 for the margin model and 0.203 for Elo. It beats chance, but not the simple models.
My priority: lock every model version before testing it and report the result as it is, even when a simpler model wins.
An error analysis showed where the simulator loses to the margin model: games with a largely new roster (0.238 vs 0.218). With stable rosters it wins (0.238 vs 0.248). It knows only a league-average level for a new player, while the margin model learns directly from results.
So I tried the obvious fixes under the same rule: tune on 2024–25, lock, test once on 2025–26. Tuned priors scored worse (0.2400 vs 0.2382). Game-to-game form made player ranges more honest, lifting 80% range coverage from 73% to 78%, but worsened the game forecasts (0.2419). Neither went into production. The same happened with the schedule correction in the power ranking: it didn’t predict later games better than plain point difference. The ranking stays because it describes the games played, with an 80% range for each team, and the page says it isn’t a forecast.
Next, I want to test whether external decision models, such as Subquadratic’s Snap and Typesafe AI’s Jev, can do better than my own. They could take over the decisions inside a possession, such as who acts and whether they shoot or pass, forecast a whole game, or generate and compare tactical and line-up options. Each one gets the same test as the current model: the same 161 games, only earlier data behind each forecast, settings locked in advance, and the result written up even if it doesn’t help. Because Sim Studio runs 1,000 simulations per game, I’ll also record what each simulated game costs. A model that doesn’t win can still show where mine falls short, and the parts that work could replace single steps in the simulator rather than the whole thing.
Also tracked in Linear: uncertainty that depends on sample size, line-ups and absences.
Statistics pages answer questions people already have. I wanted to test whether the data could also say something a basketball fan might disagree with, so I wrote the first analysis: what do five games tell you?
Over two seasons, a team’s points margin in its first five games correlated at 0.83 with its results for the rest of the season in the women’s league, and at 0.28 in the men’s. Helsinki Seagulls started 4–1 two seasons in a row, then won 85% and 37% of their remaining games. The sample is 43 team seasons, which the article says is enough for an observation, not a rule. Its figures are frozen at publication, and tests check the written claims against the data.
I used agents to map which data was available and on what terms, then to build the views. My part was deciding what good looks like and checking the result. Decisions stay in the repository: when I reject something, such as green scores on the match cards, the reason goes into an instruction file the next agent reads, so the same idea doesn’t return.
I direct most of the code rather than write it, and I want to be able to explain it. So I had an agent make me a practice site built around KorisLab: SQL, Python, and spotting AI-written queries that run but give the wrong answer. I work through it to understand why the product works the way it does, not just what it does.
The clearest example is performance. PageSpeed scored the mobile home page 38, with 20 seconds of blocking time. The agent removed one visual effect at a time and measured again. Without a GPU, one dither effect took about 12.5 of 19.5 seconds of main-thread work; with a GPU, under one second. The first report led with those no-GPU figures. I pushed back, because hardly anyone browses that way, and follow-up work is now ranked by what readers feel on real phones. The real cost was a 730 kB shader engine drawing fog that was almost invisible on a phone, so phones now get a static haze.
Each fix went into its own pull request, with before and after figures and a note on what it didn’t fix. One first version looked right on desktop but changed the phone layout. Comparing screenshots of 13 page types at both widths caught it, and those styles went back into one file. In a PageSpeed lab test, the mobile score rose from 38 to 71, Largest Contentful Paint fell from 10.4 s to 3.8 s, blocking time from 20.4 s to 480 ms, and accessibility rose from 91 to 100. The 3.8 s is still above the 2.5 s target, so the next ticket is showing the home page’s content before JavaScript runs.
Planning lives in Linear, where I can quickly note what I want to change and an agent writes it up as a ticket. The performance work left a prioritised list, each ticket saying what done means, and the same tickets can brief whichever agent picks up the work next.
KorisLab mainly uses Basket.fi statistics and distinguishes verified figures from calculated estimates. Validation is ongoing, and not every game is available to every tool. I have emailed the Finnish Basketball Association to ask about further data availability and whether this use of the public data is allowed, and have not yet received a reply. KorisLab is unofficial and non-commercial, and its about page says it isn’t an official service.
Current-season data refreshes automatically every 30 minutes through the afternoon and evening, and a refresh is published only if the build and tests pass.
Next, I want to add more detailed evidence from game video with Vire, a local-first sports video analysis engine I am developing for player tracking and validated performance data. That would add evidence the box score can’t provide, such as real movement and shot difficulty.

Learn from your own Slay the Spire decisions.
An early coaching mod for Slay the Spire 1. I am collecting gameplay data and exploring how a review of your own choices can help you improve.
View project on GitHub ↗I enjoyed playing Slay the Spire, but often did not know whether I was making good decisions. Learning from my own runs felt more appealing than searching for a YouTube video I would both want to watch and actually learn from.
That is why I started building Spire Coach. It began as a way to help myself learn, and I hope it might eventually be useful to other players too.
The current focus is gathering run data: card plays, choices and combat outcomes. For now, the experience is mainly about prompting reflection through questions, with only a small amount of rule-based feedback. It is too early to treat that feedback as a reliable assessment of a run.
The first implementation is limited to Ironclad. It records data locally and includes one narrow lesson about card order: applying Vulnerable before a Strike can increase its damage.
The aim is to explain what could have been done better and why, using the actual situation rather than generic advice. Once I have enough data, I want to experiment with Jev as part of evaluating decisions. That is a future experiment, not a feature in the current mod.
Spire Coach is intended to be a coach, not a hack or an autoplayer. It should help you understand your decisions and learn for the next run. Choosing cards, taking actions and playing the game remain with the player.

Check your availability without reaching for your phone.
A small smart-glasses concept for checking calendar availability during a conversation. The idea is to show just enough information to answer a scheduling question at a glance.
View project on GitHub ↗The planned interaction starts with a question in conversation. OpenSlot would recognise the mentioned day and automatically show its availability in the glasses. If nothing is scheduled, the display could simply say “Empty”. If there is an appointment, it could show “13:30–16:30 · Busy”, leaving the rest of the day visibly available.
The aim is a brief availability check that helps you answer while staying in the conversation.
I do not own MemoMind glasses, so I plan to develop the idea using MemoMind's desktop simulator. The project is still at the concept and early groundwork stage. A working experience with a real calendar and automatic speech recognition has not been built yet.
The scope is checking availability. I want to explore how little information the display needs to make a scheduling decision easy, without turning it into a full calendar interface. The next step is to develop that interaction in the simulator before testing it on physical glasses.
Set a scrolling limit. Complete your chosen activity to continue.
An iOS app concept designed to track only the apps a user chooses. At the planned 20-minute limit, those apps are blocked until the user completes their chosen activity. App blocking is still in development.
Outcome
MediaPipe push-up counting, the core of the unlock, works.
Onboarding is nearly done. App blocking, the piece that enforces the limit, is the last big step before the App Store.
Marked text appears only in the full case

People who want limits need a boundary they choose, with a clear way to return to the apps they value.
I lead the product direction and have done most of the research, design and development. Builder, PM and research agents support the work; product decisions remain with me.
Onboarding is nearly finished, the main-screen UI is largely complete and MediaPipe push-up tracking works. App blocking and other activities are still in development.
Brio is designed to run in the background when enabled, tracking only apps the user selects. The starting limit is planned at 20 minutes. At that point, completing a preferred activity would unlock another period of use.
Reaching the limit again would require a longer or more demanding activity. The progression is bounded: it resets within 24 hours and steps down after longer breaks. After eight hours away, for example, the next requirement should be easier than the previous one. These rules and app blocking are still being developed.
The user chooses whether to accept this guidance. Brio does not prompt them to return; stopping use should support their goal.
The first idea was to ask for push-ups or squats. I soon realised that people who cannot comfortably perform those movements can also struggle with doomscrolling. A fixed exercise requirement would exclude them from the core experience.
The decision: broaden the activities people can choose. People with limited mobility also experience doomscrolling, so the initial push-up and squat requirement needed to change.
The MediaPipe push-up tracker is implemented; the other activities remain to be developed. The direction has changed, while the broader experience is still unfinished.
The current concept is a short Guitar Hero-inspired activity, with movement prompts performed at the right time. It should make the pause clear and manageable.
A game people want to replay could work against that purpose. I am considering the edge case of someone keeping social media open or scrolling more to trigger another round. That tension guides the activity design; the minigame is not yet finished or validated.
Brio is my first product intended for the App Store. I have invested in competitor comparisons, onboarding psychology, positioning and a marketing plan. The research made me pay closer attention to how the first experience can influence continuation and payment; I have not measured Brio’s conversion yet.
I found competing products, but no exact match for the approach I wanted: social media can be useful and enjoyable, while boundaries can make its use more intentional. That informs the language and interruption. It also constrains monetisation: increasing recurring revenue should respect the reason someone uses Brio.
This creates a product and business question: how can Brio earn continued use while supporting the user’s wish to spend less time in the selected apps? The same tension appears in the activity itself, which needs to be engaging enough to complete without becoming another reason to seek screen time.
The analytics layer is planned. After release, I intend to A/B test screens and features against onboarding, conversion and more intentional use. Those experiments have not run yet. The aim is to find a combination that works commercially and remains consistent with the voluntary guidance people signed up for.
I originally planned to build Brio with a friend. Their work commitments limited their availability, so I have done most of the work. I use builder, product-management and research agents across implementation, planning and investigation. I set the direction and priorities, make the product decisions and bring their work into the same research, design and development process.
Onboarding is nearly finished and the main screen is largely complete at the UI level. Movement tracking recognises push-ups with MediaPipe, with other movements still to come. App blocking, the core of the idea, is the largest piece still unbuilt.
Release depends on completing and evaluating blocking, activity progression, recovery rules and the broader movement choices. Brio remains an iOS app concept in development.
Track every junior player’s minutes, run substitutions and plan training.
A web app for junior football and futsal coaches. Track playing time and run substitutions on a phone, tablet or computer. Plan 2D/3D football exercises on a tablet or computer, and share them as links that open on any phone.
Outcome
Every player’s minutes on record instead of counted from memory.
Two junior coaches have used it for about three months across 10–15 matches. It has helped most in tournaments, where minutes are hardest to keep even.
Marked text appears only in the full case
Two junior coaches needed a record of individual minutes they could review themselves and discuss with parents.
I designed the product and built it with coding agents. Each feature started from a coach’s request or a testing session.
Football has match management and exercise planning. Futsal has match management with its own pitch and timing; its planner comes next.
My girlfriend and her colleague coach junior football. They needed a record of each player’s minutes that they could review themselves and explain to parents when needed, so I made playing-time tracking the first priority. Both coaches tested it throughout development, and prepared lineups and exercise planning came from their requests too.
One coach had struggled with a competing app. Trying it myself, I found inconsistent interactions and too many menus, so I kept match actions close together and cut the navigation needed during a game.
Each player also has a small playing-time indicator. Two wavy lines mean the player is close to the average of the active players; an arrow up or down shows they are above or below it, so the coach sees who needs more minutes without doing the maths.
Before Peluutin, we didn’t measure playing time. Doing it by hand would have been too hard, so I glanced at the clock and tried to remember how long each player had been on. Paper works too, but it isn’t as handy or as accurate.
On a phone, the pitch, with its formations and individual minutes, and the bench don’t fit in one view. Choosing a substitute opens a compact grid of on-field players below the bench, so the coach picks who comes off without going back to the pitch.
A coach wanted lineups ready before kick-off. Saved groups now let a whole group change at once, and several players can be selected on the pitch.
After the match, the statistics page shows each player’s all-time minutes, and Excel export gives coaches something to share with parents. Goals, scorers and match history complete the record.

Substitutions stay under control, and playing time really evens out over longer stretches. One match is easy to manage, but in tournaments Peluutin has helped.
The same coaches also needed a way to plan training exercises. The Finnish Football Association software my girlfriend used would not run on her newer M-series Mac. When I tried it myself, I wanted to make planning clearer and more visual.
I built a football planner with 2D/3D views and a timeline. The timeline sets the order and duration of every pass and run, such as which player gets the ball first and how long the ball takes to travel. Without it, actions would play in a fixed order, with the first item placed moving first.
Because exercises replay, a coach can send a link to a colleague instead of writing a long message about what happens and in what order. Exercises are created on a tablet or computer, and shared ones play on a phone too. The coaches have already planned real training sessions with it.
I chose 3D because some movements are easier to read in depth, the boards around the training pitch leave room for things like club sponsors, and, honestly, I think it simply looks better.
The association’s planner only made static exercises. With replays and share links, showing what we’re going to do takes much less effort.
A coding agent built the first versions from my prompts. Agents get me 60–70% of the way; I designed the flows from the coaches’ requests, directed the detailed interaction work, and reviewed every change on a local server.
I had designed editing for a computer: Ctrl+Z, copy and paste shortcuts, area selection and the Delete key. A tablet has none of those, and one tester found removing things from the pitch too frustrating. A Layers panel now lists every player, piece of equipment and route with its own delete button, and Undo sits in the bottom bar instead of behind a shortcut.
The same session showed smaller frictions. Placing several cones in a row meant reopening the menu each time, so the drawing and placement menus now stay open, and thin lines got larger hit areas. It also exposed a sequencing bug: an action after a run or dribble started from the player’s old position, so the replay didn’t match the plan.
This tester plans in light mode, in 2D, on a plain grass pitch, so each of those is an option. She is also left-handed and found the tools more natural on the left, so the tool rail can switch sides.
A coach can share a PNG or a read-only playable link. The recipient goes straight to the exercise on a phone, without creating a team or adding players. The link carries the exercise data, which makes it self-contained but produces long URLs. Player names and numbers are omitted by default, while the coach’s original exercise stays unchanged.
Team data stays in the browser, with file export for backups on a phone or for moving data to a computer; exercises can be saved too. Sign-in will wait until I know how to store children’s personal data safely. Until then, a cleared browser cannot recover data automatically. Before adding sign-in and server-side storage, I need to clarify how player information should be stored, accessed and deleted. Shared exercises should stay viewable without signing in, and PDF export is also planned.
Basketball and floorball come next, with ice hockey a possibility. Each needs its own match workflow, because substitution rules and timing change how a coach runs a game.
Football currently includes match management and exercise planning, and futsal has its own pitch and match timing. Opt-in PostHog analytics shows visitor numbers, key actions and recurring problems, and complements coach feedback in finding flows that need work. It is collected only from visitors who accept the popup.
So far, feedback comes from two coaches over about three months. Next I want to test with more coaches and teams to see how well the workflows suit other coaching styles.
Make near-expiry work easier for store employees.
A food waste service concept connecting an employee tool, an in-store point and clearer customer-facing information.
Outcome
Expiry checks mapped shelf by shelf, so the next employee sees what’s already done.
A working prototype built from four years on the shop floor, with a one-store pilot plan and defined measures.
Marked text appears only in the full case

Store employees need to find, handle and hand over near-expiry products during busy daily routines.
I translated more than four years of grocery-retail experience into a shelf-map prototype and a connected service concept, prioritising the employee workflow before deeper app integration.
The employee tool illustrates the interaction. Store-system integration, operational feasibility and customer response still need validation.
I worked in grocery retail for more than four years. Expiry checks relied on manual inspection, employee memory, shelf familiarity and local routines. The same shelves could need checking again later, and keeping track of completed work was frustrating when the store was busy.
Discounted products already sell well in many stores. I focused the concept on supporting the work that brings those products to customers: identifying them, handling them consistently and making the remaining selection visible.
I designed the employee tool around a map of the physical shelf. Each square corresponds to one section, with status colours indicating whether it needs attention. A department view supports moving between shelf units. The intended workflow is to check the physical products, decide how to handle them and update the status for the next employee.
I envisage the tool running on handheld scanners or internal tools employees already use. A production version would still need to establish how expiry and product status information reaches it; inventory integration is outside this prototype.
My scope decision: use the first prototype to explore one repeated shelf-checking task and its handover. The operational workflow needs evidence before investment in custom hardware or a deeper app extension.
Milk shelf map - Unit 4
Dairy department > Milk > Unit 4
The proposed S-hävikki point brings near-expiry products into a consistent cooler, shelf or display near checkout or another high-traffic area. The message is practical: useful food, a clear discount and a changing selection.
Although I explored a branded installation, the first pilot should use simple signage, consistent labels and an existing product zone. That keeps the setup small enough to find out whether placement and presentation help customers notice and choose the products. S Group is the setting for the concept; the store pilot has not been carried out.
The proposed S-kaupat section would show broad, current signals about nearby stores and product categories. Near-expiry stock changes quickly: an item may sell or sit in someone’s basket before availability updates.
I therefore scoped out reservations and exact future availability. A category signal such as “available today” would need to disappear when the selection is too small or uncertain. The app extension depends on a reliable in-store process and remains secondary to it.
I would begin with one store, one or two product categories and a one-to-two-week pilot. The aim is to assess the employee workflow and customer-facing point before investing in a wider system.
These are planned measures, not results. The central assumption is that the connected workflow and display would improve handling and selection enough to justify the added work. Custom hardware, deeper integrations and personalised alerts would need evidence from that first step.
A controlled reading rhythm for dense text.
A Chrome extension that presents selected text one word at a time, exploring pace, focus and the friction of starting to read.
Outcome
For me, dense text got easier to start and faster to read.
A working Chrome extension shows selected text one word at a time at your chosen pace. Testing with other readers is still ahead.
Marked text appears only in the full case

My own reading difficulties led me to explore whether showing less text at once could make reading easier to begin and sustain.
I scoped and built the selected-text reader, then refined its launch, pacing and controls. My own use informed the iterations; testing with other readers is still ahead.
The extension reads selected text one word at a time. My own experience is encouraging in some situations; comprehension and usefulness for others remain untested.
Mild astigmatism and ADD can make sustained reading difficult for me, and I am not a particularly fast line-by-line reader. A long text can feel heavy before I start. Tahti explores whether showing one word at a time can reduce that initial visual load and provide a rhythm to follow.
In my own use, it has helped in some situations and let me move through text faster. That personal experience shaped the experiment, but it does not establish a benefit for other readers.
I built a framework-free Chrome extension that reads a selected passage in an overlay on the current page. Keeping the selection with the user limited the implementation and made it possible to explore the reading experience directly.
My scope decision: start with text the reader selects. That keeps control with the user and lets me explore the reading interaction before solving full article extraction.
Headings, images, tables, embeds and code would need their own handling. I left automatic extraction, PDFs, accounts, analytics and AI features outside the MVP.
The first version required opening the extension after selecting text. I added a right-click launch to remove that step. Within the overlay, the early layout spread settings and controls across several areas; I brought the word, progress and controls into a central stack to keep them close to the reader’s focus.
I also replaced WPM and font-size steppers with compact sliders to make the settings less visually dominant. Font size, dark/light mode and a short countdown provide basic comfort controls.
A uniform words-per-minute interval made every word advance at the same rate. I added extra time for sentence endings, softer punctuation breaks and longer words. The first words start slower and build toward the chosen pace.
Play, pause, reset and ten-word backward or forward controls let the reader stop or revisit a passage. The purpose of these adjustments is to make the pace manageable; their effect on comprehension still needs testing.
The format is related to Rapid Serial Visual Presentation (RSVP), where words appear sequentially in one position. The research provides context for the experiment, but does not validate Tahti itself. One-word presentation removes paragraph shape, easy scanning and surrounding words that a reader may need.
My next step is to use real articles, notes and documentation, then test with other readers. I want to assess ease of starting, sustained focus, fatigue and understanding, including which text types are unsuitable for this mode.
The next expansion depends on that evidence. If it helps other readers while preserving understanding, I would explore an article mode with pauses for headings, images and tables. Tahti remains a reading-support experiment; my own positive experience is not a demonstrated general speed or comprehension improvement.