Optimize

Updated Oct 15, 2026 Open the tool

Optimize shrinks FiveM and GTA V game files (YDR, YDD, YFT, YTD) so servers stream them faster. It resizes and recompresses textures, and can add missing LODs or lower a model's triangle count. Optimizing never costs credits. There are two versions: the workspace tool at /app/optimize (new, 2026-09-14) and the legacy pages at /app/optimize (still live until the owner switches them off). In /app/optimize and the converter, a file row opens its actions on right-click (Download or Retry, Remove; the converter adds Scale, Review and Apply to all), since 2026-09-22.

Optimize in the workspace (/app/optimize)

One tool for all four file types, with one set of settings. Drop files (or choose them), press Optimize, download the result. Nothing is saved to the library: files go to the conversion server and come back in the browser tab, so the right-hand panel shows the details of the picked file instead of the library.

Batch or single depends on the plan.

Plan Files per drop At once Largest file Daily limit Choose folder
Free, Plugin Pass, Booster one at a time 1 50 MB 5 delivered files per 24 hours (15 until 2026-09-27) no
Standard, Referrer 100 2 80 MB none no
Pro 500 3 100 MB none no
Max 2,000 5 100 MB none yes

Discord boosters moved from the Standard row to Free's on 2026-09-30 (only Standard and up are paid plans; until then a booster batched 100, 2 at once, 80 MB, with no daily limit).

On Free, Plugin Pass and Booster the list holds one file (since 2026-10-01). Dropping or choosing another replaces it once it is finished and downloaded; until then the new file is refused with "Download b.ydr first", and that toast's Download button saves the finished file and loads the new one in one go. While a file is on its way, a new one gets "One file at a time on Free". Several files at once keep the first ("2 more skipped · batches on any plan"). A file that came back as a kept original or a failure has nothing to download, so the next file replaces it straight away. The list's add button reads "Choose file" on these plans.

A dropped folder is walked on any batch plan (up to 500 files per drop). Only a delivered file counts against the free day: a kept original and a failure never do. The limits live in OPTIMIZE_LIMITS (src/lib/config/plans.ts); the server enforces them, not only the page. Since 2026-10-09 the free day is one per browser: files delivered by other Free, Lite or Booster accounts that signed in from the same browser count toward it (ws_optimize_start_shared, optimizePool in server/workspace/optimize.ts; workspace.md, "One Free day per browser"). Files running at once stay each account's own.

Settings. Defaults shrink a file without touching a vertex.

  • Textures (on): Max size 256, 512, 1K (default), 2K, 4K; larger textures scale down, smaller ones stay. Format: Auto (default: only ever moves to a lighter format, never drops alpha), DXT5 ("With alpha"), DXT1 ("Smallest · no alpha"), BC7 ("Sharpest"); a named format still never makes a texture heavier or loses alpha. Mipmaps (on). Neutralize tinted blacks (on, since 2026-10-07): dark almost-grey pixels made exactly grey as a texture is re-encoded, so DXT gives dark areas no green or purple cast. Here it rides along on the textures the run re-encodes anyway (a resize, a format change) and never drags an untouched DXT texture through another lossy encode; the Packs and Peds buttons force every texture through, because there the cast is the complaint (textures.neutralizeBlacks, off for an API caller who does not ask; the box's passes take True for force and "touched" for this). Later that day the owner saw the cast survive the switch and a grid of green and purple tiles across dark areas: the cast is the encoder's, not the source's (Pillow truncates block endpoints to 5-6-5, so an exact grey 21 comes back (16, 20, 16) whatever the pre-pass did, and pymateria's mip generator does the same at every level), so with the switch on the box now refits every dark-grey block to endpoints 5-6-5 holds as grey and writes the mip chain itself (see Internal).
  • Meshes (off; YDR, YDD and YFT only, a YTD skips them): Detail 40 to 100% of the full model's triangles; below 70% a warning says low detail can rip or tear the surface. LODs (on when Meshes is on) adds Medium, Low and Very low where missing; the file grows.
  • Advanced: Force size (every texture to exactly Max size; warns that small textures grow), Only smaller files (on: keep the original when a file does not shrink; LODs always count).

The game generation (legacy or Enhanced) is read from the file itself; there is no setting for it.

What each file ends as. Every row shows one of:

  • "Smaller · −74%": the new file is smaller and is ready to download.
  • "LODs added · +51%": LODs were added; bigger on disk, lighter in game. Delivered.
  • "Already optimized": nothing in the file was above the settings (textures already at or under Max size, meshes off). The original is kept. Details: "Nothing in this file is above these settings. Lower Max size, or turn on Meshes to add LODs."
  • "No embedded textures": a model whose textures live in a separate YTD. Optimize the YTD, or turn on Meshes.
  • "Original kept · no lighter" (since 2026-10-07; "not smaller" before): the file was rebuilt but is no lighter for the game — its textures cost the same loaded, no LODs were added, no triangles went. Turn off Only lighter files to take it anyway (a cast fix alone, say). What decides is what the game pays, never the file on disk (the owner, 2026-10-07: "I don't care if the file is smaller on disk, I care if it's more performant for the game"): a resource is zlib-compressed on disk, so an uncompressed texture of flat colour can weigh less there than its DXT5 re-encode while costing four times more loaded. The list, the row, the details and the totals all lead with memory ("5.3 MB → 1.3 MB", "Lighter · −75%", "12 MB lighter · 40%"); the file on disk is a second line, there for the 16 MB streaming budget.
  • "Original kept · textures unreadable": some textures have no readable mip data and the file could only be written without them, so the original is kept. The file needs re-exporting in a texture tool.
  • A failure (red), with the fix in the details panel:
    • "Cannot read": encrypted, escrow-protected or not a GTA V file. Escrow files cannot be optimized; use the unencrypted file. Since 2026-09-27 an escrow-locked upload is named as such at once, before any parsing: "is escrow-protected: FiveM's asset escrow encrypted it, and only the server that owns it can provide an open copy."
    • "Encrypted": same, detected while loading.
    • "Names use a legacy character set": rename the textures to plain letters and repack.
    • "Unnamed textures": repack with every texture named.
    • "Too complex for GTA V": a part has over 32,767 vertices.
    • "Geometry could not be processed": the mesh step crashed on this geometry. The details offer "Try without Meshes".
    • "Took too long", "Texture encoder failed", "Optimizer restarting", "Connection lost": passing faults; the tool already retried up to three times, "Try again" is offered.
    • "Over 50 MB" (or the plan's size): larger than the plan allows; "See plans".
    • "5 of 5 used today · Next at 09:40": the free day is used; the batch stops, the footer offers "Unlimited on any plan".
  • When the mesh step fails but textures worked, the file is still delivered with "Meshes skipped" in its warnings.
  • A delivered or kept file over 16 MB is flagged "Over 16 MB · may stutter when streamed".

Downloading. Each smaller file has its own download. "Download all" (footer) or "Download zip" (right panel, nothing picked) saves every smaller file in one zip, folders kept; "Include kept originals" adds the kept files unchanged so the zip replaces the whole folder. Results live only in the open tab: leaving the workspace with files not downloaded asks first.

The tray. A batch is one entry in the top bar ("Optimize 12 files", "7 of 12 · 3.1 MB saved") with Stop; when it ends it offers Download all.

Optimize over the API (Max, 2026-10-01)

Max accounts can optimize from scripts and CI with an API key from /app/api: POST https://zoov.dev/api/public/v1/optimize with the file's name and size opens a run and answers an upload link on the conversion server; the file sent there comes back optimized, exactly as in the workspace tool, with the same settings object. It is the same Optimize: the same ledger (ws_optimize_runs, tool optimize), the same Max limits (100 MB, 5 at once, no daily cap), and files running in the workspace and over the API count together toward the 5. Live since 2026-10-01. The routes, the upload links and the errors are in API keys, the automation API and the resource API.

Common questions (workspace)

Why did my file not get smaller? Either nothing in it was above the settings ("Already optimized": try a lower Max size, or turn on Meshes for LODs), or it was rebuilt and is no lighter for the game ("Original kept · no lighter"). The original is kept in both cases so a server never gets a worse file. Lighter means what the game pays: the textures cost less loaded, LODs were added, or triangles went. The file's size on disk does not decide; a file bigger on disk but lighter loaded is delivered, and the row says by how much. Files with no textures of their own ("No embedded textures") only change with Meshes on.

It says "Cannot read". The file is encrypted (FiveM escrow), damaged, or not a GTA V resource. Escrow-protected files cannot be optimized by any tool; the creator's unencrypted file is needed.

Does a failed or kept file use a free optimization? No. Only a delivered smaller (or LOD-added) file counts toward the 5 a day (15 until 2026-09-27). The Designer's Free limits (editing minutes, YTD and DDS exports, 3D previews) are a separate count and do not touch this one.

Why can I only add one file? Free, Plugin Pass and Discord boosters work one file at a time: optimize it, download it, then add the next (it replaces the first). Standard and up batch (Standard and referrers 100, Pro 500, Max 2,000).

Which format should I pick? Leave Auto. It compresses uncompressed textures and never makes one heavier or drops transparency. DXT1 only helps opaque textures.

Legacy Optimize pages (/optimize/*, redirect to /app/optimize since 2026-09-20)

The Optimize hub at /app/optimize is a drop zone that routes a file to the right optimizer. Drop a .ydr, .ydd, .yft or .ytd (an .xml variant such as foo.yft.xml also routes) and the matching tool opens with the file loaded. Toasts: "Opening Optimize Prop…", "Opening Optimize Clothing…", "Opening Optimize Vehicle…", "Opening Optimize Textures…". Any other file shows "Unsupported file" with "Drop a .ydr, .ydd, .yft or .ytd file (XML variants accepted)."

The four legacy tools: Optimize Props at /optimize/ydr, Optimize Vehicles at /optimize/yft, Optimize Clothing at /optimize/ydd, Optimize Textures at /optimize/ytd; all four now redirect to /app/optimize. Each is titled "Drawable Optimization", "Fragment Optimization", "Drawable Dictionary Optimization" or "Texture Dictionary Optimization" and needs a signed-in account.

Legacy settings: Target Resolution (128 to 4096 px, default 1024), Force Resolution, Compression Type (DXT1, DXT3, DXT5 default, BC2, BC3, None), Optimize Mesh (Ratio) 0.40 to 1.00, Change Scale (YDR only), Create Missing LODs, Preserve Mipmaps. The YDD tool can return a single YDR ("Return Model Type"); the YTD tool has per-texture skip boxes; Optimize Props can "Bundle as FiveM Resource" (Pro+). "Optimization Preview ()" shows the LOD geometry. Known legacy faults: Preserve Mipmaps is not sent on YDR, YDD and YFT; a bigger output is offered without a word; a failed run and a re-download both use a free optimization.

Legacy free limit: 60 optimizations per rolling 7-day window (FREE_LIMIT = 60), shown as "{n} of 60 remaining this week"; when hit, "Daily limit reached. Upgrade to Pro for unlimited." (wrong word: the window is weekly) and the toast "You have reached your weekly limit of 60 free optimizations. Upgrade to Pro for unlimited access." The window is anchored to the first optimization since 20260704_optimize_rate_limit_window_fix.sql; the allowance returns 7 days after that. Single file only, 50 MB per file. Standard, Referrer and Booster batch 100 files (2 at once, 80 MB), Pro 500 (3, 100 MB), Max 2,000 (5, 100 MB, folder upload). Batch messages: "Batch limit of 100 files reached (Standard Plan tier)", "Rate limited - retrying in Ns (1/5)", "Batch complete: 23/25 succeeded, 2 failed".

Languages

Since 2026-09-20 this part of the site is available in English, French, German, Spanish, Portuguese, Polish and Dutch. It follows the browser's language on a first visit, and the language menu (the icon beside the profile in /app, or in the top navigation on public pages) changes it. Nothing about how it works, what it costs or what a plan includes differs by language; only the words do. File names, format names (GLB, YDR, YTD), product and model names, code, and some error lines that come from the server stay in English. Details and known gaps: Languages and translation.

When the conversion server runs out of disk (2026-09-19)

The conversion server ran out of disk twice because request folders named like materia-api-ydd-optimize-88blfd79 piled up in its temp directory. Each endpoint removes its own folder when it finishes, but that cleanup is skipped when a person closes the tab mid-download, when a request is cancelled, when the process restarts, or when Windows still holds a file open (the old cleanup ignored that error silently).

Since zoovdev-winapi commit 1d29bd3 the server looks after itself: api/core/temp_janitor.py removes any materia-api-*, winapi-optimize-*, winapi-clothing-skin-* (since 2026-09-26) or materia-worker/<job> folder nothing has written to for 45 minutes, on startup and every ten minutes (20 minutes when under 10 GiB free, with a warning in the log). The extract cache (materia-api-extract-cache) is never removed wholesale by that rule; it has its own, below. Under 2 GiB free, uploads are refused with 507 and code insufficient_storage: "The conversion server is out of disk space. Try again in a few minutes." That message reaching a user means the disk is nearly full and staff should look; it clears on its own once the sweep frees space.

The cache budget (2026-10-02). On 2026-10-02 production ran out of disk again, this time from caches, not request folders: 10 GB in the live extract cache (converted models, uploaded vehicles kept a day for the UV and Vehicle tools, encoded textures, kept results), and 16 GB in a second extract cache in the Administrator account's temp directory that nothing had read since the API began running as SYSTEM on 2026-10-01 (its temp directory is C:\Windows\Temp). The vehicles people bring to the Vehicle tool (user_extras, 0.17 GB) were not the cause. Since zoovdev-winapi api/core/cache_budget.py, every janitor sweep holds the whole extract cache to 8 GiB (CACHE_TOTAL_MAX_BYTES), oldest entries first, and when the disk is under 12 GiB free (CACHE_MIN_FREE_BYTES) it gives back as much as it takes to reach that floor; nothing written in the last ten minutes is removed, nor ped-rigs/ or daykeys/. It also removes another account's materia-api-extract-cache once nothing in it has been written for two days (six hours while the disk is under the floor). The converted-model cache's own limit went from 15 GB to 6 GB. A removed entry costs only time: the next request converts again, or the browser is asked for the vehicle's bytes again (410), as an expired entry always did.

Staff: scripts/clean-temp.ps1 in the winapi repo is the same rule as a script (-WhatIf to look, -MaxAgeMinutes 20 when it is urgent, -Install for a 15-minute scheduled task that works even when the API is down). Settings are environment variables listed at the top of temp_janitor.py.

Retired legacy sources (2026-09-26). The legacy pages (/optimize and /optimize/{ydr,ydd,yft,ytd}, OptimizationModal.svelte, the breakdown components under components/ui/Tools/optimize, utils/optimize-limits.ts, config/faq.ts) were deleted on 2026-09-26 with the legacy retirement (the retirement paragraph in workspace.md ("The release switch", "Retired (2026-09-26)")); the whole /optimize subtree 307s to /app/optimize, which this doc describes. The v1 consume endpoint stays for the moment. Source globs dropped from this doc: src/routes/(site)/(workspace)/optimize/**, src/routes/(site)/(public)/optimize/**, src/lib/utils/optimize-limits.ts, src/lib/config/faq.ts.

Changed (2026-10-15). While a run goes, the settings column opens with a status line, "Optimizing · settings apply to the next run" ([data-optimize-busy]): every switch, slider and select below it is disabled during a run, and with no word for it 32 people a month pressed the dead switches.

Topics optimizeoptimiseydryddyftytdcompresslodmipmapbatchrate limitdaily limitkept originalnot smaller

Something missing? Ask on Discord