# Rokon Zaman > Building neat & snappy web experience. UX freak. React Geek. Build with NextJS, Scale with Go! Rokon Zaman is a Software Engineer based in Dinajpur, Bangladesh. This file contains the full markdown body of every published blog post and project description on https://www.rzamann.com. Use it for RAG, summarisation, or search ingestion. Contact: roman@rzamann.com Generated: 2026-06-07T14:00:32.686Z --- # Blog Posts ## I Rebuilt This Site for Three Years Source: https://www.rzamann.com/blogs/three-year-rebuild Date: 2026-05-16 I rebuilt this site for three years. It was supposed to be a weekend job. The old version ran on Gatsby — fine, fast enough, but I'd been hitting walls. The routing and GraphQL pipeline were rigid in a way that fought me every time I wanted to add anything new. There was no built-in image optimisation. Every new dependency landed with its own dev-server slowdown. Meanwhile, every other project I touched was on Next.js, and the contrast got worse every month. Next eventually shipped file-based routing of its own. Then `next/image`, `next/font`, `next/script`, the metadata API, `next/og`, the package-import optimiser. None of it world-changing on its own; together it stopped being a framework and started being a runtime with batteries. The first Saturday of the rewrite ported routing. The second Saturday threw out the data layer. The third Saturday ate the design. Three years later I'm writing this post on the version that finally shipped. ![81 commits, 302 files changed, +18,184 / −17,787 — the PR that finally landed.](images/pr.png) ## The App Router changed it twice Next 13 was halfway through the rewrite when it dropped the App Router and React Server Components. The whole model of "fetch the right thing at the right rendering tier" got simpler. The old per-page `getStaticProps`/`getServerSideProps` ceremony collapsed into _just await the data in the component_. SSG, ISR, dynamic — picked per component rather than per route. The leaf decides. The page stays static when none of its leaves are dynamic. That's how this site's home page is statically prerendered while the Spotify and GitHub feeds quietly fetch themselves from edge-cached route handlers. I ended up rewriting the rewrite. Worth it. ## The Spotify thing has been on my list for five years The integration on this site — the spinning vinyl, the per-track metadata, the artist play buttons, the persistent player that survives navigation — is the latest version of an idea I've had since I shipped a "now playing" widget on my GitHub README years ago. The widget did exactly one thing. Every other variation I tried since then died in scope creep. This one lives because the rewrite finally had the scaffolding to support it: a refresh-token flow that lives server-side, route handlers that edge-cache, a client provider that mounts at the layout root so playback survives client-side navigation. The pieces existed. They just needed somewhere to plug in. ## Tailwind, eventually The Gatsby site was MUI. The rewrite migrated incrementally — I kept the MUI icon package around for a long while, importing one or two glyphs at a time as I ported each section. By the time I deleted the last MUI import the design had quietly become a tailwind+css-variables system with its own pill-button vocabulary, a global `Surface` and `Paper` split, accent tokens that flip per theme. None of it dramatic on any single commit. The whole arc, in hindsight, is the most opinionated thing about the codebase. ## Turbopack made dev tolerable The really bad part of Next for a while was the dev server. Cold starts that wouldn't finish in a coffee refill. HMR that paused for thoughts. Turbopack going stable changed the calculus — startup is instant, edits propagate immediately, and the loop that had been making the rewrite feel slow stopped being slow. ## A template fell out of the rewrite Most of the time I spent on the migration wasn't moving content. It was building the chassis I wanted to live inside. A markdown content layer with auto-detected galleries, image optimisation that doesn't require you to think about it, frontmatter schemas with cleanly typed loaders, a build-time image sync, a watcher, a CLI scaffold. The same chassis I started porting into a separate project: [edgetatic](https://edgetatic.vercel.app). The Gatsby version of this exact site became a reusable template there — ["The Great Gatsby"](https://edgetatic.vercel.app/templates/great-gatsby) — built from the same content layer I was hardening here. The lineage runs both ways now; whatever the markdown layer learns on this site ports back to edgetatic as a generic primitive. ## The starfield The animated starfield in the sidebar is a small thing nobody would notice unless I pointed at it. It came from a different project entirely: [301st.xyz](/projects/301st.xyz) has a small game with a particle system called PyroStars. ![PyroStars game from 301st.xyz](images/PyroStars.png) The site's sidebar starfield is the same trick, dialed down to ambient. A canvas, a couple thousand points, parallax against scroll, a ripple on click. The "this small effect ate the whole evening" kind of thing. Months later it became the visual signature of the homepage. ## What it cost Most of what I just listed didn't need to exist. The Gatsby site would have served me. What I rebuilt was not the site — it was the shape of the codebase I wanted to live inside. A new section is now five minutes. A new content type is a frontmatter field plus a render. A new third-party integration caches itself sensibly and degrades silently when its credentials are missing. None of that ships visibly to a reader. All of it was the point. The visible cost is real. People don't see the version you don't push; they see the version you don't update. For three years the deployed site barely moved while I rearranged its skeleton in private. If you're a reader of this blog, I owe you posts. If you're someone considering a rewrite of your own, I owe you this: - **Merge sooner than you think you should.** Six months of polishing rarely beats shipping the messy version and iterating in public. - **Treat the migration as a deadline, not a habit.** Mine became the latter. I'm not unhappy with the result, but I lost three years of writing here. - **Give yourself a small reason to finish.** The thing that finally pulled this in was adding a tiny links page. Not the point of any of the work — just the smallest finishable surface on the pile. The moment the rewrite was scoped around shipping that, the rewrite shipped. The version you're reading this on is the one I finally pushed. It is not the version I intended in 2023. It is better than that one, because three years of trying things in private — even the things that didn't pay off — left a codebase I can move quickly in now. Posts to follow. I have a lot of them. --- ## Spotify and Me Source: https://www.rzamann.com/blogs/spotify-and-me Date: 2026-05-13 Spotify is turning 20 this year. I've been around for **7 of those** — even though they only officially launched in Bangladesh 5 years ago. So yeah, I was that guy, jumping through VPN hoops just to keep the green app on my phone. What started as a music app quietly became the thing that gets me to my desk. ## The rules I set on day one I didn't stumble into this. From the very beginning, I made a deal with myself: Spotify isn't background noise, it's a **work trigger**. Three rules, written into my head before I hit play the first time: - **Only songs.** No music videos. No YouTube rabbit holes. - **Only when working or learning.** No music while scrolling, walking, or lying in bed. - **Headphones on means deep focus.** If I can't commit to that, I don't get to hit play. It sounds rigid. It is. That's the point. Music became the carrot — and to earn it, I had to get up, drop the phone, sit at the desk, and start. You'd be surprised how often a 3-second ritual is the entire reason a 3-hour session happens. ## Why desktop, always Free Spotify on phone is basically unusable. Locked to shuffle, ad every few tracks, no skips, no queue, no rewind. They're herding you toward Premium and they're not subtle about it. Desktop is a different product entirely. Even on the **free plan**: - Unlimited skips and queue - Drag-and-drop into any playlist, from anywhere - Global media keys to **pause, skip, and like — without ever leaving my editor** - Ad breaks short enough not to break flow Controlling Spotify while coding, **without ever opening the app**, is the part I'd miss most if I switched. YouTube Music, Apple Music — they've caught up on the catalog, but the plumbing is still nowhere close. Connect just works. Playlists feel like real folders. Keyboard shortcuts are first-class. I eventually went Premium and never thought about ads again. But I want to be clear: the habit was already built on the free desktop tier. ## 7 years, in numbers Over the years I've stacked up: - **Dozens of playlists** — sorted by mood, time of day, project, and which year of my life I was in - **Hundreds of artists** discovered, thousands of songs added - A heavy lean toward **progressive house, melodic, upbeat, energetic, chill, and EDM** - Very little sad or moody stuff — I want music that *moves* me forward, not sideways If you saw my library you'd probably guess I drive somewhere with the windows down. I don't — I sit very still and write code. The bounce is internal. ## The numbers Spotify gave me back **17,017 songs.** First song streamed: *Better When You're Gone* by David Guetta, Brooks & Löote, on **February 16, 2019**. An astronaut on the cover, a melodic-house beat, and a chorus you don't really listen to because you're trying to figure out where to put the comma in the next line of code. That track set the tone for everything that came after. Most-streamed artist all time: **Ellie Goulding — 6,256 minutes**. That's over **4 days of my life** spent inside her catalog. She wasn't even on my radar before Spotify, and now she's the soundtrack to entire eras of work. You could probably reconstruct my last 7 years if you mapped my Wrapped year by year. Every December I get a quiet little diary I didn't know I was writing. ## What I actually built I thought I was building a music habit. I was building a **focus trigger**. Now, the second I hear the first note of a familiar deep-work playlist, my body knows what comes next. Posture changes. Tabs get closed. The internal "let me just check Twitter" voice gets quiet. Pavlov was right about more than dogs. I don't think Spotify *made* me successful. But the **ritual I built around it** has carried more of the weight than I'd like to admit. ## The takeaway If you're trying to do focused work and it isn't sticking, try this: pick one small input — a song, a coffee, a desk lamp — and make it the only door into your work. Then never use it for anything else. Spotify just happened to be mine. Happy 20th, by the way. 🎧 --- ## StarWalls: A Minimal Space Photo Wallpaper PWA Source: https://www.rzamann.com/blogs/starwalls-a-minimal-space-photo-wallpaper-pwa Date: 2026-04-21 # Building StarWalls: A Space Photo Wallpaper PWA I wanted a better way to browse NASA photos on my phone and save them as wallpapers. NASA has the APOD API, the Image of the Day RSS feed, and the full Images API — tons of great content — but no mobile interface that makes it easy to frame and download a shot for your lock screen. So I built one. Here's what I learned building it, including the parts that didn't work the first time. --- ## The Stack Next.js 16 App Router, Tailwind CSS v4, Framer Motion. Three data sources: the APOD daily API, the NASA Image of the Day RSS feed, and the NASA Images API filtered for Artemis II mission photos. The App Router was the right call mostly for ISR. The gallery's first page pre-renders at build time, so the initial load feels instant even on a slow connection. Server components handle the data fetching, which keeps the client bundle lean. --- ## The Gallery Infinite scroll with virtualization. The hook has a sentinel pattern: when `initialPhotos` is passed in as a prop from the server component, it skips the first client-side fetch entirely. Something like: ```ts if (page === 1 && initialPhotos?.length) return; ``` Without this, the client refetches page 1 after hydration, flashes the same content, and looks broken for a beat. The principle is bigger than the line: ISR'd data should be authoritative until something changes it, and the client has no business asking again on mount. VirtuosoGrid was my first instinct for the grid virtualization. It doesn't SSR items — it needs the DOM to measure — so that wasted a round trip. Ended up with a simpler intersection-observer approach that plays better with ISR. The service worker handles offline with stale-while-revalidate per resource type. API routes get short cache with background revalidation, images get a year-long immutable cache, navigation falls back to a cached shell when offline. --- ## The Photo Detail — The Fun Part Clicking a thumbnail opens a full-screen detail view with a spring-based morph animation: the image expands from the thumbnail's bounding rect to fill the screen. The same swipe UX that drives the photo detail — the vertical strip, the gesture tracking, the `isFilled` zoom state — is also what powers TV Slideshow mode, so that infrastructure ended up doing double duty. Three photos live on three slides sharing a single `stripY` MotionValue — swipe up for the next photo, swipe down for previous, swipe right to close. I wrote this badly twice before it worked. The three-slide strip isn't three separate components. It's one strip that translates vertically. Keeping a single MotionValue instead of individual animation states means the gesture tracking stays consistent — you're always moving one thing, not coordinating three. --- ## The Bugs That Taught Me Things **The router re-render bug.** I wanted the URL to update as you navigate between photos so deep links work. Used `router.push()`. This triggers Next.js page-level re-renders, which unmounts and remounts the photo detail component between cycles. The `originRect` — the bounding box captured on click that the morph animation needs — was getting wiped before the animation could start. The morph never played. The fix was to stop treating it as navigation. The browser already exposes a way to change the URL without disturbing the React tree — the History API. The component never knows the URL moved; the share button still copies a deep link. It felt wrong at first to step around the framework's router. But this isn't a page transition — it's an animation-driven UI state that happens to be reflected in the URL. Two different concepts, and conflating them was the bug. **The `useSearchParams()` drain.** Any component that reads search params opts itself — and everything above it — out of static rendering. The fix is to push that read as far down the tree as possible and isolate it behind a Suspense boundary, so only the parts that actually depend on the URL pay the cost. Easy to miss until you check your build output and see what was supposed to be static suddenly marked dynamic. **Deep links to photos not yet loaded.** Open the app on a link to photo #200 when the gallery streams 24 at a time and the auto-open guard finds nothing to open. The naive answer is to keep paging until you find it; the right answer is to admit defeat on the list and just fetch the photo by id. The list and the detail are different concerns — pretending one always contains the other was the mistake. --- ## The Canvas CORS Rabbit Hole The wallpaper cropper lets you frame a photo for a specific screen size, then download it. To do that I need to draw the image to a canvas and call `toBlob()`. NASA images don't set `Access-Control-Allow-Origin` headers. Drawing a cross-origin image to canvas taints the canvas, and `toBlob()` throws a `SecurityError`. The browser will happily display the image in an `` tag, but you can't touch the pixel data. The escape hatch is to put yourself in the middle: a route on your own origin that fetches NASA's image server-side and streams it back. The browser doesn't care that the bytes ultimately came from somewhere else — same origin, no taint, canvas works. The proxy is small enough to feel like cheating, and that's the point — the whole rule was about *who served the bytes*, not what's in them. Second issue: `navigator.share()` with files. The share API only works inside the original user-gesture window, and that window quietly closes during your `await`s. Proxy fetch, canvas draw, blob conversion — by the time you actually call share, the browser has decided you don't have permission anymore. The lesson here is more about JavaScript than about sharing: anything that depends on a user gesture has to *happen synchronously* with the gesture, or it's already too late. Treat the native share as a bonus, not the contract; a plain download link is the real interface. --- ## PWA and Play Store Full web app manifest with shortcuts, maskable icons, and screenshot slots for the Play Store listing. The service worker strategy per resource type matters: you don't want to cache API responses the same way you cache images. Images are immutable (NASA isn't editing that APOD photo from 2019), so a year-long cache is fine. API data is live, so short cache with background revalidation. Shipping to the Play Store as a Trusted Web Activity is mostly an exercise in proving you are who you say you are. Android wants a signed declaration, served at a fixed path on your own domain, that links the installed APK to the website it wraps. Once that handshake is in place, the TWA opens straight into the PWA — no browser chrome, no install banner, just the app. --- ## TV Mode Adding `?tv` to any gallery URL turns it into a kiosk slideshow. Photos advance automatically in zoom-fill mode every 15 seconds, looping back to the first photo at the end. It works on any gallery — `/?tv`, `/artemis-ii?tv`, whatever. Point a TV at it, walk away. The URL design is composable. `?i=30` sets the interval in seconds. `?photo=2024-01-15` starts from a specific photo. These stack: `/?tv&i=60&photo=2024-01-15` is a valid, shareable link that anyone can open and get the exact same slideshow. No login, no settings screen. Space, Enter, or the middle button on a TV remote pauses and resumes. Arrow keys skip forward or back and restart the timer. Escape closes back to the gallery. The remote support wasn't extra work — the gallery already handled arrow keys for keyboard navigation, and that infrastructure carried over. The implementation is simpler than it sounds. The 15-second autoplay is a `setTimeout` that resets on every photo change. That reset was already happening — the existing navigation strip uses `id` as a dependency to reset its position when the current photo changes. The slideshow timer just plugged into the same reactive pattern. No new state machine. What made it easy was that `isFilled` — the zoom-to-fill toggle already in the photo detail — was already a first-class piece of state. TV mode just sets it to `true` on open and resets to `true` on every photo change. The swipe strip, the keyboard nav, the zoom state: all of it was already there. TV mode is about 40 lines of glue around existing infrastructure. It's the kind of feature that emerges when you've built the core mechanics well enough that the edge cases are easy. --- ## What I'd Do Differently **Separate URL state from UI state from day one.** I conflated them and paid for it with the router bug. The URL is for sharing and history. The UI state is for the current session. They overlap but they're not the same thing. **Don't fight the router for in-component navigation.** If it's a real page transition, use the router. If it's an animation-driven UI state change that happens to update the URL, use `pushState`. **Pick the animation library before designing the data flow.** Framer Motion's `AnimatePresence` and `usePresence` require you to think carefully about component mount/unmount timing. If I'd thought about that earlier, the morph animation design would have been cleaner from the start instead of being refactored twice. The app works well. Fast initial load, smooth animations, the cropper does what it's supposed to do. NASA's content is genuinely beautiful and having a clean way to browse and frame it for wallpapers was worth the yak shaving. Code is on GitHub if you want to look at the proxy route or the sentinel pattern in more detail. --- ## 2020 Wrapped Source: https://www.rzamann.com/blogs/2020-wrapped Date: 2021-01-15 ## Intro Welcome to my first blog. A brief of what I did in 2020. It was a horrifying year for all the people all over the world for sure. But it shouldn't hold you back. This year I indeed taste a lot of new things, thanks to the pandemic effect. Mostly React, NodeJS & JamStack related. ## Prequel I started learning React in early 2018 but I was new to JavaScript (and ES6) at that time, and I had a hard time understanding some of the React concepts. So I spend more time understanding modern JavaScript. I also learned to program in C for a little bit to get closer to the silicon! Then some Python for the sake of college. Besides those, I keep playing with JS/Node. In 2019 I mostly played games for the sake of games 😄 (wasted 2019). But I learned a little of bit C# and Java in and out of college. ## Section main Coming in 2020, started doing React with a better understanding. Found hooks a !god bless feature but feels weird (I liked the class way of thinking)! Found a nice UI library Material-UI. I was introduced to Jamstack and Gatsby, started building this site with Gatsby and material-UI. Thanks to Gatsby's out-of-the-box support, finally started using TypeScript = ❤️. The reason I choose Gatsby over [NextJS](https://nextjs.org) was GraphQL, and the plugin ecosystem. Next was little overkill for this website. But GraphQL can be a pain in the a**. I start understanding Java & C# better after using TypeScript. Because I had more experience in JavaScript. I use [repl.it](https://repl.it) to quickly try different languages without having any local setup. And this situation where I found myself learning Gatsby, Graphql and TypeScript at the same time. And it's a bad thing. (that's an advice) I wanted to try game development. I wanted to try the Godot game engine. But I choose PhaserJS. After learning game dev fundamentals with phaser/web games I can move to a proper game engine like Godot or Unreal Engine if I want to! That was the plan, phaser as a middleman.😮 Late 2020, I finished a Codecademy course on Phaser. Re-write a ['Game'](https://github.com/romanrokon/codey-in-tundra-ts) from that course using TypeScript. There are a lot of things to deal with to develop a game than web/app development in general. Hoping to release a AAA title soon, alone.😂 But we don't live in a perfect world, do we! Then some experiment with React-native, Flutter and Electron. I started to use Now/Vercel to deploy node API to the cloud, which lead me to serverless/lambda. Fell in love with the concept of serverless computing. ## Outro My 2020 goal was to learn the [MERN](https://www.mongodb.com/mern-stack) stack. I learned JAMstack, serverless in addition to MERN. Haven't done any MERN project to showcase here yet. I feel like I'm gonna do more serverless stuff. Wanted to wrap up my 2020 but wrapped until 2020! The 2021 Journey begins. (wait, 2021 started a [while ago!](https://wow21.vercel.app/while%20ago)) If you're a passionate developer, working with similar technologies, reading this very last line, let's make something, together! 🙄 \*Pardon any grammatical mistake. --- ## Hello World Source: https://www.rzamann.com/blogs/hello-world Date: 2020-12-31 ### Basic Markdown Styling # Heading 1 ## Heading 2 ### Heading 3 #### Heading 4 ## Paragraph Lorem, ipsum dolor sit amet consectetur adipisicing elit. Optio dicta facere ducimus voluptatibus omnis quibusdam a commodi provident minus perferendis dolor officiis, obcaecati numquam cupiditate impedit, placeat quae! Doloremque, amet. ## Code block ```ts const theme = 'Cyber Dark'; // available for VSCode ``` ## List - The quick brown fox jumps over the lazy dog - The quick brown fox jumps over the lazy dog ## Image ![markdown image](/images/backdrop.webp 1280x852) --- # Projects ## EyeHack Source: https://www.rzamann.com/projects/eyehack Date: 2026-04-30 # EyeHack A macOS menubar app that enforces the **20-20-20 rule**: every 20 minutes it hijacks every display for 20 seconds so your eyes get an unavoidable break. ## What it does - Sits silently in the menubar — invisible while you work. - Every 20 minutes triggers a full-screen overlay across all connected displays. - Overlay randomly rotates between three break modules (never repeats the same one twice in a row): - **Distance Illusion** — a hybrid optical illusion whose image shifts depending on viewing distance, training the eye to refocus. - **Sci-Zen** — a minimal breathing prompt. - **Brain Reset** — a riddle with a delayed answer reveal. - Skips breaks automatically when the screen is recording, the camera is active, or a meeting app is holding the display awake. - Resets its timer after sleep, so there's no surprise break at 9 a.m. - Plays a completion sound when the break ends. - No dismiss-early button. ## Stack | Layer | Tech | |-------|------| | Backend | Rust, Tauri v2 | | Frontend | React 19, TypeScript, Vite | | Styling | Tailwind CSS | | Package manager | pnpm | ## Meeting / recording detection Breaks are skipped when any of these are true: | Signal | Detection | |--------|-----------| | Screen recording active | `ioreg` — `CGSIsCapturing` key present | | Camera in use | `pgrep -x VDCAssistant` succeeds | | Meeting app holding display awake | `pmset -g assertions` contains `PreventUserIdleDisplaySleep` + a known app name | Apps checked: Zoom, Google Chrome, Brave Browser, Teams, Slack. ## Sleep awareness The 20-minute timer uses `SystemTime` (wall clock) rather than `Instant` (monotonic, which pauses during sleep). If the gap between ticks exceeds 3 minutes the system was asleep — the accumulator resets and gives a fresh 20-minute window after wake. ## Auto-updates Install once and forget. New versions arrive on their own — the app quietly checks at launch and offers to update. There's a "Check for Updates…" item in the tray menu if you're impatient. --- ## StarWalls Source: https://www.rzamann.com/projects/starwalls Date: 2026-04-10 # StarWalls: A Modern Space Photo Wallpaper PWA Discover the cosmos with stunning NASA imagery, perfectly framed for your device. I wanted a better way to browse NASA photos on my phone and save them as wallpapers. NASA has the APOD API, the Image of the Day RSS feed, and the full Images API — tons of great content — but no mobile interface that makes it easy to frame and download a shot for your lock screen. So I built one. --- ## 301st.xyz Source: https://www.rzamann.com/projects/301st.xyz Date: 2025-12-01 The ultimate interactive countdown creator. While we started as the internet's favorite New Year's timer, you can now track any moment —birthdays, vacations, or product launches—with our stunning 3D visuals. - ✨ Interactive Fireworks - 🔗 Create & Share - 🔴 Stream Ready - 🌍 Global & Offline --- ## Next big thing Source: https://www.rzamann.com/projects/next-big-thing Date: 2023-12-01 😎 --- ## New Testament Source: https://www.rzamann.com/projects/new-testament Date: 2022-11-01 ## Work in progress Powering the official documentation of Edgetatic. Made with NextJS App Router and MUI/Joy-UI. --- ## Material You Source: https://www.rzamann.com/projects/material-you Date: 2022-03-01 #### Key Points - Markdown powered content - No CMS - Perfect Lighthouse scores - Your color your feel - The about page is your resume - Optimized & Responsive for every screen - Everything is damn simple yet unique - Dark mode support --- ## SMDB Source: https://www.rzamann.com/projects/smdb Date: 2021-06-01 ## Work in progress ### Key features - Check movie ratings, reviews - Watch trailers - Find Where to watch them (or even pirate them (🤐)) - Build your collection of - Watchlist - Watched Recommend to others - Discover popular movies from the feed - Discover from other's collection --- ## YourTube Source: https://www.rzamann.com/projects/yourtube Date: 2021-05-01 ## Work in progress ### Expected features - Download youtube videos at any resolution - Download as audio - Stream in the background without downloading - Simple integrated media player - Proper audio tag for music files - Sync your library with you - Consumes less battery juice - Ability to download directly from the youtube app - Clean, simple and junk free --- ## UniFonts Source: https://www.rzamann.com/projects/unifonts Date: 2021-04-01 It's a [TWA](https://developer.chrome.com/docs/android/trusted-web-activity/overview/) app, that you can download on Android). Chrome is required to run properly. ##### It Generates weird or fancy fonts 𝕵𝖚𝖘𝖙 𝖑𝖎𝖐𝖊 𝖙𝖍𝖎𝖘 ### Key Features - Lightweight - Favorite fonts collection - Simple, Elegant, Ergonomic UI - 🤩 The core library of this app is [open source](https://github.com/romanrokon/lib-unifonts). You can get it from NPM with `npm install lib-unifonts`. ### Download the App now (tap the Android button below) Is this a landing page now? --- ## Gatsby Portfolio Source: https://www.rzamann.com/projects/portfolio Date: 2020-06-01 UPDATE: My old portfolio site converted to a reusable template! Learn more [Here](https://edgetatic.vercel.app/templates/great-gatsby) This is a statically generated Gatsby site. With markdown file-based content system. There is no CMS or database. Gatsby is doing all the magic with Graphql. Routing is actual file paths in the source tree. And you might be feeling the great 'Gatsby' speed! --- ## Pixearch Source: https://www.rzamann.com/projects/pixearch Date: 2020-05-01 - React Context/Hooks API for global state management - React Router - Pixabay free images API - Progressive Web App (PWA) You can click on the cloud icon below, And look for an Install or 'Add to home screen' prompt. ### Key Features - Find and download free stock photos - Search exactly what you need with comprehensive filters - User/system preferred light/dark mode - Material Design - 🤩 --- ## Amusic Source: https://www.rzamann.com/projects/music-player Date: 2019-01-01 This thing gets buried inside YourTube 😉 --- ## reSearch Source: https://www.rzamann.com/projects/research Date: 2018-06-01 - Easy search engine selector - Clean and minimal design - Daily wallpapers - Chrome extension for new tab page --- ## upShot Source: https://www.rzamann.com/projects/upshot Date: 2018-05-01 This was my second javascript project. The goal was to do a Photoshop-like interface. And photo manipulation using CSS filters. It was more like a CSS challenge! Later I simplified the design. Improved functionality with html5 canvas API implementation with a third-party library. I may rebuild it in react/native in the future. --- ## haloBot Source: https://www.rzamann.com/projects/halobot Date: 2017-10-01 This was my first project in Javascript that I did in the late 2017. This is the sort of stuff came to mind when learning conditional statements. Using switch statement to respond according to different input values. It's just some hard-coded text response, no magic or AI. But it was fun! Over the years, I integrated different APIs like DuckDuckGo Instant Answers and OpenWeatherMap. It helped me to learn a lot about AJAX and async/await features, Web speech API, and DOM manipulations etc. ### Key Features - Voice recognition - Text to speech - Weather - Instant answers - Chat like