The Start of Something New
Oct 14, 2025
Gabriel Wilk
Slowing down is speeding up
This past summer, I got the chance to launch the Amarillo App at the echo park rising festival in Los Angeles. I’ve told people about it in brief spurts of conversation where I condense the product and technology into a 30 second summary of what the thing was, and how it came to be the data solution for the entire festival. Not that I don’t enjoy catching folks mid-bite of a delicious Joy Ride pizza at an SF tech meetup - But I just need to tell my story in a longer format that allows me to dive deeper.
Despite the saturated bursts of short lived content consumed regularly online, it’s important to appreciate and remind ourselves of longer timescales. Things like human relationships, personal growth, and trust are not achieved with short bursts of dopamine. If we don’t make space to deeply explore a subject, to focus, reflect and learn, what sort of value are we actually adding to our lives? In the midst of the endless feeds of bliss, it’s possible that the highest value thing to do is none of it at all. Flow state is a rarity, and the more I’m able to tap in, the more healthy and fulfilled I feel. This can happen by composing music, writing code, or writing this blog post. I don’t care what I do anymore - If I’m in flow, I know it’s exactly what I should be doing.
Going to Meetups
During the beginning of the AI hype, when Convex was the “backend for AI”, they were hosting a bunch of vibe coding meetups at their office. I was hanging out at one of these when Wayne (the head of community at Convex) said “Ok Gabe, your turn to demo.” I hadn’t really prepared anything and wasn’t feeling super confident in my code that day. But I tried something new, I said, “Ok I’ll try this new thing out where I just demo one of my projects and not really care or put any effort into my presentation”. So I demoed Bandlink - the very first project I developed on Convex back in September 2024, which allowed bands to make profiles and post their upcoming performance and creative projects.
I didn’t like my presentation. I felt like the app looked like shit on Desktop. But I had to remember that I was doing this new thing where I didnt care anymore, and my notoriously harsh self judgement had to fuck off. Anyway, the final remarks of my presentation were something like “whatever.” I awkwardly walked away, wearing a veneer of post-demo mediocrity. But when I got back to my seat, I realized the spotify API integration I had written the week before, which allowed bands to look up their spotify artist by name and bind the spotify image URLs to their profiles in Convex, had actually worked. I had just demoed a working product. After the demos were done, Elliott (who I had met that day), came up to me and said “Dude, that was amazing. I have to connect you with my friend Kamran in LA.” And that’s how it all started.
Crushing Code
The functional paradigm of React made a huge impact in the Javascript echosystem - Declaratively expressing UI as a function of State changed the game for the way clientside code could be written. But the server remained isolated - protected by the Software gods with a network boundary. Inaccessible by design - only the people who endured the pain of managing cloud infra could deploy a change to a property in your GraphQL schema.
The declarative patterns in React could never - ever - come to the backend. But then came Convex, which shattered these assumptions of how software should be built. They allowed developers (me) to deploy backend infrastructure with Typescript functions. True Infrastructure as code - a set of primitives expressed in functional programming. I don’t know if I can properly express the gravity of this innovation. In the first few weeks of jamming with Kamran and Elliot on this new software idea, I was able to move fast and come to our weekly demos with working prototypes. It was so much fun - and the fastest iteration cycles I’d experienced. After experiencing several “aha” moments together, a new horizon was surfacing. With the Echo Park Rising festival only a month away, it was time to crush some code.
The Three Dimensional Problem
During our initial phase of development, it became clear to me that the relationships Kamran was navigating in organizing a three day music festival were pretty complicated. Getting the data model to feel right both in code and natural language would be no small feat, and in order to do it, we had to distill these relationships into their most basic anatomies. Only then, could we determine our database tables and convex schema with a high level of confidence that we were developing on the right abstractions.
We broke down the data model into these foundational types:
Entity
A collection of users with roles + permissions and a bespoke singular identity. A coffee shop, band, venue, chamber of commerce, are all entities
Program
An occurrence at a point in time, like a performance
Series
A collection of programs
The magic of the system happens when we can express all three data types in relationship with each other. For example, The “Echo Park Chamber of Commerce” entity would be hosting the “Echo Park Rising 2025” series, which would be comprised of a collection of programs created by different venues (entities). This was a clear expression in natural language, but was a challenge to express in a relational data model, because a many:many SQL table join represents a 2D mapping of information.
Lucky for me, I love math, and have been studying Linear algebra on Youtube with the 3Blue1Brown "The Essence of Linear Algebra" course. I realized that if I represented two database tables as documents with nodes along an XY plane, I could represent the relationship between two rows in different tables as a vector sum in a 2D vector space (2D coordinate). If I then introduced a third resource in this data model, I could expand the visualization to a 3D vector space, where a relationship between three documents could be thought of as a vector sum in a 3D vector space (3D coordinate). This meant that I could express this in a relational database schema with 3 table joins forming three 2D vector spaces (2D slices) that all join at the origin.
I might sound crazy, but the shit worked. Here is how the 3 table joins ended up looking like in the convex schema (note: a “team” in the schema is what we defined as an Entity in our definitions and shared language).
programs_teams_roles: defineTable({
programId: v.id("programs"),
teamId: v.id("teams"),
startAtInMsSinceEpoch: v.number(),
roleId: v.id("roles"),
})
.index("by_program", ["programId"])
.index("by_team_and_start_at", ["teamId", "startAtInMsSinceEpoch"]),
programs_to_series: defineTable({
programId: v.id("programs"),
seriesId: v.id("series"),
startAtInMsSinceEpoch: v.number(),
})
.index("by_program", ["programId"])
.index("by_series_and_start_at", ["seriesId", "startAtInMsSinceEpoch"]),
team_series_roles: teamSeriesRoleSchema,
Smart Indexes Make Efficient Queries
Being at the festival was awesome, and I realized that as the festival continued, the query performance on the festival’s schedule page improved. The festival page (series) queried the backend for programs by series Id. Back to our epic 3D data model, this is a lookup where we query our series_to_program join table, get the program IDs, and then fetch those programs in parallel. But we add a bit of extra sauce - we use the start_at property of a program as an actual index on this table join. We leverage this index by calculating a memoized time stamp on the client with a react ref, and then by sending it as an argument to the backend query. Here is the timestamp:
const memoizedTimestamp = useRef(toZonedTime(Date.now(), "UTC").getTime());
And here is the client-side query made to the backend with convex’s useQuery hook:
const data = useQuery(api.series.publicHandlers.listPrograms, {
seriesSlug: Route.useParams().seriesSlug,
memoizedTimestamp: memoizedTimestamp.current,
filters: {
...(tags && tags.length > 0 && { tags: tags as Array<Tag> }),
...(type && { type: type as ProgramType }),
...(ages && { ages: ages as Ages }),
startAtInMsSinceEpoch,
endAtInMsSinceEpoch,
...(locationId && { locationId: locationId as Id<"locations"> }),
},
});
As time progressed, the window of time between start time and end time of the festival closed, which meant that fewer and fewer documents were in the index range of the first lookup. This was a huge win, and allowed the performance of the system to continuously improve as more and more traffic was coming to the website during the festival.
What I Did Wrong
The biggest inefficiency of the system was the way I treated (or didn’t treat) the images. Every entity in the system uploaded hi res images for their profiles and programs, which meant that I had heavy images in storage. For file storage usage in convex, this was totally fine. But because I didn’t resize the images, it caused an insane usage of convex file bandwidth, and cost me a lot of money. (homer simpson moment) DOUH. Remember, we were running 3-4 queries per second to the realtime database during the festival, and I simply overlooked this potential issue during development. Thankfully the festival ended, the traffic stopped, and now I can improve the system.
What I’m currently doing to address the problem is resizing the images on upload, and storing a smaller copy of the images in convex file storage. Then, the web client and convex functions can source the smaller images - this should greatly reduce the usage of file bandwidth. The other thing that I’m doing is optimizing that image on the client, but that’s purely a client side optimization that won’t have an effect on convex usage.
Bootstrapping
With a background as a musician and independent artist, bootstrapping is all I know. I’ve always believed in doing things slow and steady - if I take on a debt I typically just get too stressed out. Having said that, there are great angels and investors out there, and the YC model with SAFE documents on pre-equity investments can be wonderful for founders. I think it’s just about doing whatever makes sense and feels right between your cofounders given the mission at hand. We want to introduce something entirely new to the music industry, and because of the abstractions we use, we don’t need to be a high growth startup to do it. We want to design a business that can grow at a reasonable pace with sound economics. It’s more important to me to value the relationships we're building over money, and to make the best product we can without compromising creative direction. We’ve got some more festivals now that are interested in our product - and this is incredible news for us. These festivals are paying us to develop our product. Hopefully the next one involves 80% less work, and before you know it, there might just be a solid business here. It’s just fucking awesome to get to work on this stuff, and for now just gotta keep doing excellent work with a relentless optimism. This is the Amarillo way.
About the Authors
Gabriel Wilk
Amarillo Studios
San Francisco, CA
USA
Gabriel Wilk is a developer, musician, and founder of Amarillo Studios.
View Website