Last time we spent the whole episode knocking on doors -- open calls, juries, festivals, the funnel of submitting your work and living with the noes. And right at the end I told you a diffrent kind of thing happens sooner or later: instead of you offering your art to the world on your terms, someone comes to you and says "I want you to make me a piece". On their brief. For money. That flips the whole game around, and today we get into it -- commissions and client work, the business side of creative coding that nobody warns you about.
I'll be straight with you, this was the part I was least prepared for. I could write a flocking system in my sleep, but the first time a client asked "so what's your rate and when can you deliver?" I completely froze. So this episode is the stuff I had to learn the hard and slightly embarassing way -- how to take a vague brief and turn it into a real project, how to price it without either starving or scaring the client off, how to manage expectations so the work doesn't balloon forever, and how to actually deliver an installation that survives without you standing next to it. Allez, let's turn art into a job that pays.
Here's the first thing to understand: a client almost never knows what they want in the terms you need. They'll say "make it feel alive" or "something modern, but warm". That's not a failing on their part, it's just the gap between their language and yours. So the very first move, same as everywhere in this arc, is to catch the fog in a record before it drifts away:
// a brief is always vague. capture it AS THEY SAY IT, then translate later.
const brief = {
client: "Kaffee Noord",
wants: "something alive on the wall behind the bar",
vibe: ["warm", "organic", "not too busy"],
hardConstraints: { screen: "1080x1920 portrait", runsHours: 14, sound: false },
budgetHint: "small, it's a cafe",
deadline: "2026-11-20",
};
Notice I split the woolly stuff (wants, vibe) from the hardConstraints. The vibe words are negotiable and I'll interpret them. The constraints are not -- a portrait screen is a portrait screen, and "no sound" means no sound no matter how gorgeous my audio-reactive idea was. Writing them down separately, on day one, is what stops a misunderstanding turning into a rebuild three weeks later. When I skipped this early on I once built a whole piece in landscape because I assumed. Never again.
A brief in vibe-words can't be quoted, scheduled, or finished, because you can never prove it's done. So the real skill is translation: turn each fuzzy want into a concrete, checkable deliverable. I literally sit with the client and we do this together, out loud:
// translate each vibe-word into something you can actually BUILD and CHECK.
function scope(brief) {
return {
deliverables: [
{ item: "generative wall piece, portrait 1080x1920", done: false },
{ item: "warm palette locked with client (3 options shown)", done: false },
{ item: "runs 14h unattended, auto-restart on crash", done: false },
{ item: "one revision round after first preview", done: false },
],
excluded: ["sound", "touch interaction", "content changes after sign-off"],
};
}
That excluded list is the most valuable thing in the whole document, and it's the one beginners leave out. Writing down what you are not doing is how you protect yourself from the dreaded "oh, and could it also just...". Scope isn't only the yes-list, it's the no-list, and the no-list is where projects live or die. See where this is going? Every fuzzy word becomes a row you can point at and say "yes, that's done" or "no, that was never in scope".
Right, the scary one. How much do you charge? When I started I priced by pure panic -- I'd guess a number, feel it was too much, and halve it. Bad idea. The saner way is to estimate the actual work in days, then price the days. Break the project into tasks and add honest time to each:
// estimate in DAYS of real work, task by task. be honest, then add a buffer.
const tasks = [
{ name: "concept + palette exploration", days: 2 },
{ name: "build the generative system", days: 4 },
{ name: "installation + on-site testing", days: 2 },
{ name: "revision round", days: 1 },
];
function estimateDays(tasks, bufferPct = 0.3) {
const raw = tasks.reduce((sum, t) => sum + t.days, 0);
return Math.ceil(raw * (1 + bufferPct)); // 9 raw -> 12 with buffer
}
That 30 percent buffer is not padding, it's realism. Every single project hits something you didn't foresee -- the venue's screen is a weird resolution, the client goes quiet for a week, the auto-restart script fights the OS. If you quote the raw number you will work the buffer for free, which means you took a pay cut for being optimistic. I learned to bake the buffer in and never apologise for it. Then you turn days into a price with a day rate:
// price the days. a project fee reads more professional than an hourly number.
function quote(days, dayRate = 350) {
const fee = days * dayRate;
return {
fee,
deposit: Math.round(fee * 0.4), // 40% up front, before you write a line
onDelivery: Math.round(fee * 0.6),
};
}
console.log(quote(12)); // { fee: 4200, deposit: 1680, onDelivery: 2520 }
Two things here that took me years to do without flinching. First, quote a project fee, not an hourly rate -- clients relax when they know the total, and it stops them watching the clock. Second, that deposit. Always take money up front, before you start. A deposit isn't distrust, it's how you know the project is real and the client is committed. The ones who balk at a deposit are almost always the ones who'd have vanished before paying anyway. Protect your time -- it's the one thing you can never make more of.
Facing the client with a number makes the stomach flip, so I stopped writing pricing emails as prose and started generating them from the numbers I already had. Data in, calm sentence out:
// read the quote back as a plain, confident sentence. no waffling, no apology.
function quoteText(project, q) {
return [
`"${project}" -- a generative piece built to your brief.`,
`Project fee EUR ${q.fee}, split ${q.deposit} to start and ${q.onDelivery} on delivery.`,
`Includes one revision round. Additional rounds billed separately.`,
].join(" ");
}
Look at that last line. "Additional rounds billed separately" is a single sentence that has saved me hundreds of unpaid hours. It quietly tells the client that revisions are finite and valued, without any awkward conversation. Say it once, in writing, up front, and you never have to have the difficult version of that chat later. Being clear early is a kindness to both of you.
This is the trap that eats new freelancers alive, so let me be blunt about it. Without a limit, "just one more tweak" repeats until you resent the work and the client thinks you're slow. The fix is to make revision rounds a counted resource, exactly like the funnel state machine we built last episode, only now it's protecting your time instead of tracking submissions:
// revisions are a finite resource. count them down, don't let them run forever.
function makeRevisions(included = 1) {
return { included, used: 0 };
}
function requestRevision(rev) {
if (rev.used >= rev.included) {
return { ...rev, status: "billable", note: "extra round -- send a small quote first" };
}
return { ...rev, used: rev.used + 1, status: "included" };
}
When the included rounds are spent, the function doesn't say no -- it just flags the next round as billable. That's the healthy stance. You're not refusing to change anything, you're saying "of course, and that's a small extra". Clients respect that far more than an artist who silently does endless free work and grows quietly bitter. I've never once lost a client by pricing an extra round. I've lost plenty of evenings by not doing it.
A single far-off deadline is where trust goes to die, because the client can't see progress and you can't feel it either. So I break every project into milestones with dates, and I share them. It turns one terrifying cliff into a set of small, checkable steps:
// break the project into visible milestones. the client sees progress, you feel it.
const milestones = [
{ name: "concept + 3 palettes", by: "2026-11-01", state: "done" },
{ name: "first working preview", by: "2026-11-08", state: "in-progress" },
{ name: "revision applied", by: "2026-11-14", state: "todo" },
{ name: "install on site", by: "2026-11-19", state: "todo" },
];
function nextMilestone(ms) {
return ms.find((m) => m.state !== "done") || null; // the one thing to focus on now
}
That nextMilestone helper is almost embarrassingly simple, but it's what I look at every morning -- one clear "this is the thing" instead of a vague cloud of everything. And sharing these dates with the client does something subtle and lovely: it makes them a partner in the timeline instead of an anxious outsider wondering if you've disappeared. A client who can see the next milestone rarely sends the "just checking in?" email, because they already know exactly where things stand.
Now the part where creative code gets hard in a way gallery submissions never are. Way back in episode 114 we designed interactive installations, and here's where that pays off, because a commissioned piece usually has to run in the real world, on real hardware, unattended, for hours -- often in a cafe or a lobby where nobody knows what a terminal is. It cannot crash and stay crashed. So the single most important line of code you write for a client isn't in the artwork at all, it's the watchdog around it:
// the client's piece must survive YOU not being there. wrap the sketch so a crash self-heals.
function runResilient(startSketch, { onError } = {}) {
function boot() {
try {
startSketch();
} catch (err) {
if (onError) onError(err); // log it somewhere you can read later
setTimeout(boot, 2000); // wait a breath, then bring it back to life
}
}
boot();
}
That setTimeout(boot, 2000) is the difference between a piece that dies at 3pm and greets the owner as a black screen, and one that flickers for two seconds and carries on. In a gallery you're there to babysit. In a client's cafe you are not, and the piece has to look after itself. This one wrapper has earned me more repeat work than any clever shader, because what a client actually buys is not the art, it's not having to think about it.
Screens that run for fourteen hours a day have another enemy: burn-in and heat, from pixels that never change. So for an unattended piece I quietly shift everything by a hair over time, and dim it deep in the night when nobody's watching:
// long-running screens burn in. drift the whole image slowly + dim it overnight.
function screenSaver(ctx, w, h, frame, hour) {
const dx = Math.sin(frame / 900) * 8; // gentle wander, a few px
const dy = Math.cos(frame / 1100) * 8;
ctx.setTransform(1, 0, 0, 1, dx, dy); // never sit on the exact same pixels
const night = hour >= 1 && hour <= 6;
ctx.globalAlpha = night ? 0.4 : 1; // ease off when the room is empty
}
Neither of these is glamorous, and neither shows up in the pretty preview the client falls in love with. But they're exactly the details that separate a hobby sketch from something you can hand over and invoice for. The art is the easy half. Making the art survive the world is the half they're really paying you for.
When you walk away from an installation, something will eventually go wrong -- a power cut, a cleaner unplugs the machine, an update reboots it. The client can't call you at midnight, and honestly you don't want them to. So every delivery includes a dead-simple runbook: the three or four things a non-technical person can do to bring it back. And I generate that runbook from data too, so it can never drift out of date:
// hand over a runbook a NON-coder can follow. generate it so it stays honest.
function runbook(steps) {
return steps
.map((s, i) => `${i + 1}. If ${s.when}: ${s.doThis}`)
.join("\n");
}
console.log(runbook([
{ when: "the screen is black", doThis: "hold the power button 5s, wait, press again" },
{ when: "it shows a desktop, not art", doThis: "double-click the 'Start Art' icon" },
{ when: "colors look frozen", doThis: "unplug the little box, count to ten, replug" },
]));
Write that runbook for the tiredest, least technical person in the building, because that's exactly who'll be standing in front of the black screen on a Saturday night. A one-page card taped inside the counter has saved me more emergency call-outs than any amount of robust code. Kindness to the client here is really kindness to future-you, who gets to keep sleeping.
Making the art is only half a business. Getting paid is the other half, and it needs the same discipline you give the code. A commission moves through money-states, and if you don't track them, invoices genuinly fall through the cracks -- I've been the idiot who delivered a piece and forgot to send the final invoice for a month. So, exactly like the submission funnel last episode, money is a little tracked flow:
// money has states too. track them or you WILL forget to send the final invoice.
const payFlow = {
quoted: ["deposit-paid", "declined"],
"deposit-paid": ["delivered"],
delivered: ["invoiced"],
invoiced: ["paid", "overdue"],
};
function advancePay(job, to) {
const allowed = payFlow[job.stage] || [];
if (!allowed.includes(to)) throw new Error(`illegal: ${job.stage} -> ${to}`);
return { ...job, stage: to };
}
The illegal-move guard means a job can never reach "paid" without passing through "invoiced" -- your records stay honest by construction, which is the whole reason we keep reaching for this pattern. And once every job is a tracked record, you can surface the one thing that quietly wrecks freelancers: money that's owed but hasn't landed:
// surface what's owed. an unpaid invoice you forgot about is money you gave away.
function outstanding(jobs) {
return jobs
.filter((j) => j.stage === "invoiced" || j.stage === "overdue")
.reduce((sum, j) => sum + j.onDelivery, 0);
}
Run that over your jobs once a week and you'll never again be surprised by a client who "thought they'd paid". It's not distrust, it's just bookkeeping, and bookkeeping is what turns a hobby that occasionally makes money into an actual practice you can live on. Makes sense, right? The art earns nothing until the invoice is paid.
Last piece, and it's the one artists forget until it bites them. When you make something for a client, who owns it? Can you show it in your portfolio? Can they reprint it on a thousand t-shirts? If nobody wrote it down, you'll disagree at the worst possible moment. So I settle it up front, in plain fields, as part of the deal:
// rights are a fight waiting to happen. settle them in writing, in plain words.
const rights = {
clientGets: "display the piece at their venue, indefinitely",
clientCannotDo: "resell, merch, or alter the code without a new agreement",
artistKeeps: "copyright + the right to show it in my portfolio",
credit: "'generative piece by femdev' shown or linked at the venue",
};
That artistKeeps line matters enormously for your career, because the portfolio we built two episodes back is fed by exactly this work -- if you sign away the right to even show a commission, you've done invisible labour that can never build your name. And the credit line is how a paying gig quietly turns into your next paying gig, because people who see the piece in the wild ask "who made this?". Sort the rights before you start, in friendly plain language, and a commission becomes a thing that pays you twice: once in money, once in reputation.
vibe words from the hardConstraints, and write both down on day one. Assumptions are where commissions quietly go wrongSo that's the whole shape of client work, from catching a foggy brief to getting paid and keeping your name on the piece. And the thing I most want you to take away is this: the code was never the hard part. Managing the relationship -- the expectations, the money, the boundaries -- is the real craft, and it's one you build the same way you built flocking or noise, by doing it, getting it a bit wrong, and doing it better next time. Treat clients with clarity and treat your own time as precious, and commissions become one of the healthiest ways there is to fund a creative practice.
Here's the thread forward. Everything today assumed it was you, the coder, delivering to a client. But some of the best creative work happens when you're not working alone at all -- when a coder sits down with a dancer, a musician, an architect, a scientist, and the piece becomes something none of you could have made on your own. That's a completely diffrent skill, part technical and part human, and next time we get into what it really takes to build across disciplines. 't Was plezant to talk shop with you today :-).
Sallukes! Thanks for reading.
X