When I co-founded a small tech company (SiteArcade) I found myself coding alongside my engineering partner out of need. While that ignited my engineering spark, it truly flamed up when AI started transforming creative fields in 2022.
My background as a graphic designer helped me realize how generative AI can amplify human creativity rather than replace it. I realized I wanted to be an active part of this new era as a builder, not just a consumer.
Today I craft end-to-end applications that use AI to enhance human experience. I’m attracted to projects that push the boundary between creativity and code, and I’d like to share my passion through apps that make people’s day-to-day easier and more imaginative and joyful.
• Go
• Python
• TypeScript / JavaScript
• React
• Svelte 5 / SvelteKit 2
• SQL
• Gen AI integration
• Multi-modal prompt engineering
• Data manipulation
An AI storytelling engine that automatically feeds a fiction blog.
I created an imaginary art museum inspired by supernatural and weird fiction, where every object in the collection has an uncanny story to tell.
I built the backend in Go. I love the language and I believe it's great to streamline complex pipelines with a lot of moving pieces, and keep you safe from debugging hell.
For the frontend, I took advantage of Ghost CMS. Although their API comes with some caveats, it allowed me to tailor a solid publishing workflow fast.
When it comes to AI, I use three different APIs: Claude for story generation, OpenAI for self-editing, and Leonardo for images.
The backend is deployed in Fly.io and this project follows a CI/CD pipeline via Github Actions.
My goal was twofold. First, I wanted to create a codebase that acted as a true human-AI creative collaboration. Second, I worked to produce generative stories where the quality remained high and consistent enough to publish with minimum to none oversight.
Taking full advantage of gen AI without giving up uniqueness and creative control
Simply asking an LLM to "generate a museum artifact story" would produce generic, predictable content. I solved this by developing a "madlibs" style prompt generator that combines predetermined word lists with randomized selection. The system pulls from curated lists of time periods, materials, and cultural contexts that all fit the Ravensfield Museum's aesthetic. This approach gives me controlled randomness - each story feels fresh and unexpected, but never strays outside the boundaries of what makes sense for the collection.
Maintaining a consistent structure and style
Without careful guidance, each generated article could feel like it came from a different source entirely. I solved this by creating detailed system prompts that acts like a style guide. It ensures every piece feels authentic to the same fictional universe, even when generated independently.
Generating coherent content
When I initially split content generation into separate requests (text, image, and vision), the stories and visuals didn't quite align, creating jarring inconsistencies that broke the readers' immersion. I solved this by merging everything into one Claude request with a structured JSON response that includes both the article text and the accompanying image prompt. This ensures the narrative and visual elements are conceived together.
Orchestrating Multiple AIs
Managing three different AI services (Claude, Leonardo.AI, and OpenAI) meant dealing with different APIs, rate limits, and failure modes all in one workflow. I tackled this by using goroutines for parallel processing, and implemented structured error handling where each service gets its own retry logic tailored to its particular quirks.
Avoiding AI hallucinations and missteps
I added fallback logic to my content generation, to double check all the pieces need to create a new article are there, and place a new request if the AI has missed the target. I also added an extra step which uses a new request to ChatGPT that acts solely as an editor based on my instructions.
Automating content publishing
As my goal was to let my app run unsupervised, I had to spend some time building proper HTTP status code validation and GitHub Actions failure states. This prevents broken articles from slipping through and alerts me when the system needs attention.At the moment, only 1 in 20 articles fail, so I comfortably let my app self-publish with minimum oversight using a Github Actions cron job.
I would like to develop a tailored frontend in React instead of depending on Ghost. Also, I'm always experimenting to find novel ways to make generative content more original and surprising through prompting alone.
An AI chatbot with a sci-fi twist.
This app is the first intergalactic random chat platform! Users can log in and meet all sorts of characters with unique backgrounds, quirks, and personalities.
I developed the backend in Python via FastAPI. The database is SQLite3 managed through sqlmodel and sqlalchemy. I coded the frontend in Svelte 5 / Sveltekit 2. I added magic link logic for auth.
The chatbot is created using the OpenAI API and the images through Leonardo.
This app is deployed in Fly.io in one container and has a Github Actions CI/CD pipeline.
I wanted to transform an AI chatbot into a unique and joyful world building experience. Lots of users are already enjoying chatting to AI characters, so I wanted to follow that thread and nurture it into a whole self-contained universe with its own particular flavor.
Getting reliable AI generated data
Since most of my app's content comes from AI, I couldn't trust the responses to always match my database structure. I solved this by using structured responses with Pydantic schemas in my OpenAI requests. This way the response always mirrors my database models without parsing wonky JSON or dealing with missing fields.
Keeping chat messages in sync everywhere
I needed messages to appear instantly for all users while also building a permanent chat history. On the frontend, I used Svelte stores that automatically sync with localStorage. On the backend, WebSocket connections while simultaneously saving them to the database. When they work together, users see messages instantly via WebSocket, but if they refresh or reconnect, the frontend pulls from localStorage for immediate display while the backend catches up with the full message history.
Making Python and TypeScript play nice
API responses needed to match perfectly between my FastAPI backend and SvelteKit frontend, but one typo could break everything. I created matching schemas on both sides and added Valibot validation to catch mismatches before they reach users.
Magic Link Authentication
Coordinating secure sessions between frontend and backend was tricky. My approach was to keep the backend and frontend role well defined and harmonious. My backend generates JWT tokens, emails magic links, and set HTTP-only cookies for security. The frontend handles the login form and automatically redirects authenticated users, while the layout server checks cookies to protect routes.
Single Container Deployment
I was determined to deploy backend and frontend in just one container, which added an extra layer of complexity. I ultimately achieved it by using supervisord to manage all processes with NGINX routing.
I would like to make the app a bit more performant and cut the loading time. I would also like to add e2e test coverage, and incorporate it into the CI/CD pipeline. On the design front, I would love to experiment with AI video avatars to add real-time animation to the chat.