Dev.to WebDev 🛠 Dev 👁 0 📖 7 min read

The AI Cost of Marionette

When we introduced Marionette 5, one reply summed up the general mood: ‘Everybody who knows what marionette is has stiff knees and creaky backs.’ Fair enough. By the vibes, Marionette lost a long time ago. In that post

The AI Cost of Marionette

When we introduced Marionette 5, one reply summed up the general mood: ‘Everybody who knows what marionette is has stiff knees and creaky backs.’ Fair enough. By the vibes, Marionette lost a long time ago.

In that post we described how we decided whether Marionette was still worth investing in. The usual advice was about Backbone’s age, community size and hiring pools, and all of it assumed a human would write the code. So we asked the agents which ecosystem they’d want to work in. They liked Regions, predictable lifecycles and clear ownership. But that was the agents’ opinion, and opinions are vibes too.

Then Nolan Lawson asked, ‘Did anyone think to ask the agents?’ He had an agent build the same app in twelve stacks and measured what each one cost and shipped. Marionette wasn’t one of them.

So we ran his benchmark task with Codex, Marionette included, and published the results. Last time we asked the agents what they thought. This time we measured what they did.

What we ran.

The same small Pokémon app: listings, search, filters and detail pages. Four independent Codex builds in each of six stacks: Marionette, Vanilla, React Router, SvelteKit, Nuxt and Angular. This is our Codex cohort, not an extension of Nolan’s model results.

A small task gives us something concrete to repeat: the same requirements, four independently generated apps per stack, and a common acceptance suite. It also leaves room for a closer look at how each agent chose to organize the work.

Agents inherit the internet’s preferences along with its examples, good and bad. Popularity may influence what they reach for; this experiment does not measure their training data or prove that explanation. It measures what they built here.

We keep Nolan’s results order: cost, time, tokens, delivered quality and source size. Charts show all four applications, their median and their full range. This DEV edition includes the full data tables below; the original article has interactive charts with sorting and per-build values.

01 / COST

What did the agent use?

Hypothetical API proxy per build

USD proxy. Four independent builds per framework. Median (minimum–maximum); ranges are observations, not confidence intervals.

Calculated under Standard, short-context API assumptions. Not measured cost, subscription spending or an invoice. Actual dollar charges are unavailable.

Framework Median (range) b1 b2 b3 b4
Vanilla 0.2512 (0.2465–0.2856) 0.2554 0.2465 0.2470 0.2856
Marionette 0.3107 (0.2887–0.3337) 0.3311 0.2903 0.2887 0.3337
React Router 0.3470 (0.2635–0.4943) 0.4943 0.2635 0.3200 0.3740
Nuxt 0.4150 (0.2875–0.4827) 0.4215 0.2875 0.4085 0.4827
SvelteKit 0.4171 (0.3862–0.4909) 0.3983 0.4359 0.3862 0.4909
Angular 0.4478 (0.3823–0.5413) 0.5105 0.3850 0.5413 0.3823

At the stated token prices, Marionette’s median equivalent is about 31 cents. Vanilla is about 25 cents, React Router 35, SvelteKit and Nuxt 42, and Angular 45. This is a hypothetical token-only comparison, not what our subscription charged. The distinction matters more than the pennies.

What the dollar estimate does and does not mean

The calculation uses $2 per million uncached input tokens, $0.10 cached and $10 output, the previously verified Standard short-context tariff for the reported model. It assumes those rates apply; per-request context lengths, effective service tier, extra tool fees and separate child usage are not established. Actual subscription debits are unavailable. It is not an invoice.

Generation time per build

minutes. Four independent builds per framework. Median (minimum–maximum); ranges are observations, not confidence intervals.

Framework Median (range) b1 b2 b3 b4
Vanilla 4.66 (4.36–4.93) 4.93 4.79 4.36 4.53
Marionette 5.07 (4.12–5.57) 5.57 4.12 4.84 5.30
React Router 5.55 (4.45–6.55) 6.55 4.45 4.82 6.27
SvelteKit 6.08 (5.33–7.19) 6.20 7.19 5.96 5.33
Nuxt 6.92 (4.51–7.26) 6.74 4.51 7.10 7.26
Angular 6.97 (5.88–8.01) 7.40 6.54 8.01 5.88

Marionette’s median generation took 5.07 minutes, between Vanilla’s 4.66 and React Router’s 5.55. The full ranges overlap. The timer spans the native agent turn, including its tools and self-tests, but not setup, independent acceptance or Lighthouse. It does not establish a framework’s intrinsic development speed.

Tokens per build

million tokens. Four independent builds per framework. Median (minimum–maximum); ranges are observations, not confidence intervals.

Framework Median (range) b1 b2 b3 b4
Vanilla 0.961 (0.903–1.057) 0.987 0.936 0.903 1.057
Marionette 1.118 (0.991–1.383) 1.383 0.998 0.991 1.238
React Router 1.509 (0.924–1.859) 1.859 0.924 1.287 1.730
Angular 1.681 (1.414–2.556) 1.921 1.442 2.556 1.414
SvelteKit 1.819 (1.688–2.065) 1.941 2.065 1.698 1.688
Nuxt 2.145 (1.113–2.542) 2.254 1.113 2.036 2.542

Token totals are final cumulative root-thread input plus output. Cached input is already part of input and is not counted twice. Reasoning detail is not added again. A large cached context and fresh output carry different assumed prices, so token count and the dollar proxy are not interchangeable rankings.

02 / QUALITY

Four working apps.

All four Marionette applications passed all 54 independent acceptance cases. So did the retained peer applications. The comparison starts with working software: listing, searching, filtering and navigating the same Pokémon dataset.

Measurement note: The route and scoring weights were the same for every app. SSR builds could benefit from server warmups, while client-side requests were subject to browser throttling. The results also reflect how each generated app loads its data.

Loading measurements

Framework CLS Performance Accessibility
Marionette 0.111 96 100
Vanilla 0.1465 92.5 100
React Router 0 98 100
SvelteKit 0 100 100
Nuxt 0 99 100
Angular 0.109 79 100

Home JavaScript transferred

KiB. Four independent builds per framework. Median (minimum–maximum); ranges are observations, not confidence intervals.

Framework Median (range) b1 b2 b3 b4
Vanilla 4.48 (4.30–4.66) 4.30 4.46 4.66 4.50
Marionette 25.56 (23.75–25.73) 25.61 23.75 25.52 25.73
SvelteKit 36.67 (36.48–36.85) 36.85 36.71 36.48 36.62
Angular 76.72 (49.00–95.75) 95.75 75.93 49.00 77.52
Nuxt 82.57 (76.01–82.69) 76.01 82.58 82.69 82.56
React Router 115.66 (114.31–115.73) 115.62 114.31 115.73 115.69

Marionette transferred a median 25.6 KiB of home JavaScript, smaller than the framework peers and larger than Vanilla.

Home Total Blocking Time (TBT)

ms. Four independent builds per framework. Median (minimum–maximum); ranges are observations, not confidence intervals.

Framework Median (range) b1 b2 b3 b4
SvelteKit 0.000 (0.000–0.000) 0.000 0.000 0.000 0.000
Vanilla 0.000 (0.000–224.000) 0.000 224.000 0.000 0.000
Marionette 4.500 (1.000–13.000) 1.000 8.000 1.000 13.000
React Router 5.500 (0.500–51.000) 5.000 51.000 0.500 6.000
Nuxt 45.750 (8.000–78.000) 8.000 78.000 60.500 31.000
Angular 870.428 (206.000–1,031.500) 983.073 757.783 206.000 1,031.500

Home Largest Contentful Paint (LCP)

ms. Four independent builds per framework. Median (minimum–maximum); ranges are observations, not confidence intervals.

Framework Median (range) b1 b2 b3 b4
Vanilla 789.801 (773.069–848.322) 805.136 848.322 773.069 774.466
Angular 1,646.265 (1,438.886–1,989.485) 1,545.782 1,746.748 1,989.485 1,438.886
SvelteKit 1,685.932 (1,223.039–1,693.380) 1,223.039 1,693.380 1,683.142 1,688.722
Marionette 1,883.280 (1,864.987–1,893.796) 1,893.796 1,884.787 1,864.987 1,881.773
Nuxt 2,003.539 (1,849.128–2,086.339) 2,086.339 1,849.128 1,985.076 2,022.003
React Router 2,197.548 (2,117.151–2,236.093) 2,230.330 2,164.766 2,117.151 2,236.093

Home performance was 96 [92–97]; every route’s automated accessibility median was 100. Those scores are not a complete accessibility audit. Home CLS was 0.111. These charts cover home loading. All six routes are retained in the downloadable data.

All six routes.

Median [minimum–maximum] across four application medians. Each application contributes five measurements per route. The warm-server/cold-browser warning above applies throughout these loading comparisons.

Performance score

Framework Home Filtered home Pokémon Move Ability Type
Marionette 96 [92–97] 96 [92–97] 75 [74–81] 96 [95–97] 96.5 [96–97] 96.5 [96–97]
Vanilla 92.5 [91–97] 94.5 [91–97] 94 [82–96] 96.5 [95–97] 97 [97–97] 97 [97–97]
React Router 98 [97–99] 98.5 [98–99] 87 [67–90] 99.5 [98–100] 99 [99–99] 99 [99–99]
SvelteKit 100 [100–100] 100 [100–100] 98 [72–98] 99 [99–100] 100 [100–100] 100 [100–100]
Nuxt 99 [97–99] 99 [99–99] 82.5 [69–97] 95.5 [92–98] 99.5 [99–100] 98.5 [97–99]
Angular 79 [73–93] 95 [85–96] 73.5 [49–80] 73.5 [70–95] 95.5 [95–96] 83.5 [75–96]

LCP (ms)

Framework Home Filtered home Pokémon Move Ability Type
Marionette 1,883.3 [1,865–1,893.8] 1,891.4 [1,872.1–1,908.2] 6,118.6 [4,410.6–6,360.8] 1,903.2 [1,093.9–1,949.4] 1,547.1 [1,098.2–1,994.8] 1,874.8 [1,185.9–1,978.7]
Vanilla 789.8 [773.1–848.3] 801.1 [771.2–839.9] 2,529.6 [2,294.9–4,275.3] 800.6 [779–1,077.1] 1,256.9 [804.8–2,014.6] 801.2 [782.5–801.4]
React Router 2,197.5 [2,117.2–2,236.1] 2,112.1 [1,941.8–2,197.7] 2,858.3 [2,842.2–2,999] 1,360.5 [1,193.9–1,620.5] 2,015.3 [1,989.6–2,058.5] 1,904.3 [1,843.5–1,917.3]
SvelteKit 1,685.9 [1,223–1,693.4] 1,677.7 [1,150.6–1,687.3] 2,286.3 [2,274.1–3,545.4] 1,882.5 [1,148.7–1,968.1] 1,682.6 [1,113.5–1,704.2] 1,682.2 [1,092.1–1,749.6]
Nuxt 2,003.5 [1,849.1–2,086.3] 1,842.4 [1,608.1–1,983.9] 3,679.9 [2,424.9–4,932.4] 2,233.2 [2,144.7–2,251.5] 1,754.8 [1,385.6–1,841.5] 1,846.4 [1,822.8–1,996.7]
Angular 1,646.3 [1,438.9–1,989.5] 1,678.5 [1,426.6–1,979.2] 4,949.9 [2,595.3–7,242.2] 1,767.9 [1,427.6–2,261.8] 1,483.4 [1,397.8–1,917.7] 1,527.8 [1,406.7–1,981]

TBT (ms)

Framework Home Filtered home Pokémon Move Ability Type
Marionette 4.5 [1–13] 0 [0–0] 37 [18–72] 98 [55–137] 0 [0–0] 29 [15.5–29.5]
Vanilla 0 [0–224] 0 [0–4.5] 0 [0–115] 100.5 [53–160.5] 0 [0–0] 0 [0–9]
React Router 5.5 [0.5–51] 0 [0–12.5] 287.1 [152.5–805.5] 45.5 [43–54] 0 [0–6] 0 [0–0]
SvelteKit 0 [0–0] 0 [0–0] 2.5 [0–575] 36.5 [5.5–65] 0 [0–0] 6.5 [0–34]
Nuxt 45.8 [8–78] 40.5 [16–63.5] 145.5 [81–191] 129.5 [42.5–274] 44 [9–89.5] 63 [32–161.5]
Angular 870.4 [206–1,031.5] 126.4 [90–255] 456.5 [76–1,783.3] 1,527.9 [112.5–2,343.2] 82.5 [45–138.5] 573.4 [71–921.8]

CLS

Framework Home Filtered home Pokémon Move Ability Type
Marionette 0.111 [0.103–0.172] 0.111 [0.103–0.172] 0.111 [0.103–0.111] 0.111 [0.103–0.111] 0.111 [0.103–0.111] 0.111 [0.103–0.111]
Vanilla 0.1465 [0.103–0.193] 0.1465 [0.103–0.193] 0.102 [0.101–0.111] 0.107 [0.103–0.111] 0.107 [0.103–0.111] 0.107 [0.103–0.111]
React Router 0 [0–0] 0 [0–0] 0 [0–0] 0 [0–0] 0 [0–0] 0 [0–0]
SvelteKit 0 [0–0] 0 [0–0] 0 [0–0] 0 [0–0] 0 [0–0] 0 [0–0]
Nuxt 0 [0–0] 0 [0–0] 0 [0–0] 0 [0–0] 0 [0–0] 0 [0–0]
Angular 0.109 [0.008–0.134] 0.1145 [0.107–0.204] 0.1075 [0.004–0.118] 0.0595 [0–0.115] 0.113 [0.107–0.118] 0.113 [0.107–0.118]

JavaScript transfer (KiB)

Framework Home Filtered home Pokémon Move Ability Type
Marionette 25.56 [23.75–25.73] 25.56 [23.77–25.73] 25.56 [23.75–25.73] 25.56 [23.75–25.73] 25.56 [23.75–25.73] 25.56 [23.75–25.73]
Vanilla 4.48 [4.3–4.66] 4.48 [4.3–4.66] 4.48 [4.3–4.66] 4.48 [4.3–4.66] 4.48 [4.3–4.66] 4.48 [4.3–4.66]
React Router 115.66 [114.31–115.73] 115.68 [114.36–115.73] 115.66 [115.3–115.73] 115.63 [114.41–115.78] 115.63 [114.36–115.78] 115.66 [114.36–115.78]
SvelteKit 36.67 [36.48–36.85] 36.66 [36.52–36.85] 36.65 [36.48–36.85] 36.67 [36.48–36.85] 36.68 [36.52–36.85] 36.68 [36.48–36.85]
Nuxt 82.57 [76.01–82.69] 82.52 [75.96–82.69] 82.52 [75.96–82.69] 82.52 [75.96–82.69] 82.52 [75.96–82.69] 82.52 [75.96–82.69]
Angular 76.72 [49–95.75] 76.72 [49–95.75] 76.72 [49–97.64] 76.72 [49–79.61] 76.72 [49–79.61] 76.72 [49–79.61]

03 / SOURCE SIZE

Maintainability

Formatter-normalized nonblank source lines

lines. Four independent builds per framework. Median (minimum–maximum); ranges are observations, not confidence intervals.

Framework Median (range) b1 b2 b3 b4
Vanilla 820.0 (771.0–865.0) 825.0 771.0 815.0 865.0
SvelteKit 952.5 (940.0–981.0) 940.0 946.0 959.0 981.0
Nuxt 993.0 (981.0–1,031.0) 997.0 1,031.0 981.0 989.0
Marionette 1,012.5 (950.0–1,053.0) 998.0 950.0 1,027.0 1,053.0
React Router 1,050.0 (1,009.0–1,167.0) 1,029.0 1,071.0 1,167.0 1,009.0
Angular 1,551.5 (1,343.0–1,890.0) 1,890.0 1,343.0 1,392.0 1,711.0

Marionette’s median was 1,012.5 nonblank lines, ranging from 950 to 1,053. It sits close to Nuxt and React Router in this cohort, with Vanilla and SvelteKit below it and Angular above. The chart’s table includes every build.

How source lines were counted

All 24 applications were formatted with Prettier 3.9.10 and Svelte plugin 4.1.1 under one file-selection policy. Counts include comments and exclude blank lines, generated output and supplied fixture files. Original source was not modified.

SvelteKit b2 is 946 lines under this policy. Counting its supplied design-token stylesheet would add 69 lines; that stylesheet is excluded consistently, including its whitespace-only variant. This is a formatted source-size comparison, not a complexity measure.

A little less guessing.

Codex built four working Marionette applications. The median generation took just over five minutes, with a token-only API equivalent of about 31 cents. That gives us a more concrete answer than a framework’s age, popularity, or an agent’s opinion about it.

For this task, Marionette took less median agent time and a lower estimated token cost than each framework peer, while delivering the same tested behavior. Vanilla was leaner still. For a framework that supposedly lost on vibes, that is a useful showing.

Marionette 5 Extra Strength bottle: for framework fatigue, stiff knees and creaky backs.

Stiff knees, maybe. But the agents didn’t seem to mind. We’re still considering launching Buy Me an Ibuprofen.

Data and method.

Download all verified per-build metrics (JSON) · Download the full six-route assessment and methodology

The downloads below contain the verified per-build assessment. The full evidence package on GitHub includes all 24 applications, their timing and usage receipts, acceptance results, all 720 Lighthouse samples, and the normalized source analysis.

Four applications per framework is a small sample. Each route metric is the median of five mobile Lighthouse samples per application; repeated measurements do not create more independent apps. Codex CLI 0.166.0 reported gpt-6.1-sol, with effort and tier overrides unset, under 4 CPU / 8 GiB container limits.

Collection dates and dependency versions differ. Hosted Marionette documentation changed between the first and second builds, though it stayed stable within each build. No MCP tool invocation was observed, but that does not establish identical exposure. Host activity, model variation and generated architecture limit causal conclusions.

The same route, browser settings and scoring weights apply to every app. Two HTTP warmups create a warm-server/cold-browser measurement: SSR loaders can warm server API caches, while fetching a client-only shell does not run its JavaScript. All SSR apps expose artwork in initial HTML. These are measurements of the generated loading architectures, not isolated framework overhead. The local API hostname mapping also differs in some retained peer measurements, with unquantified impact. Peer values remain unchanged.

Task and results sequence: Nolan Lawson · original benchmark source. The full results and application sources and methodology are available on GitHub.

📰 Read the original article on Dev.to WebDev

Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.