Skip to content

[codex] optimize texture uploads before storage - #49

Merged
mikechaves merged 2 commits into
mainfrom
mike/texture-compression-storage
May 25, 2026
Merged

[codex] optimize texture uploads before storage#49
mikechaves merged 2 commits into
mainfrom
mike/texture-compression-storage

Conversation

@mikechaves

Copy link
Copy Markdown
Owner

Summary

  • Add a client-side texture preparation pipeline that downscales large valid image uploads to a 2048px max dimension and compresses JPEG/WebP textures before embedding them as data URLs.
  • Update texture upload toasts, telemetry metadata, and helper copy so creators understand large maps are optimized before storage.
  • Add focused unit and Playwright coverage, record the change in the changelog, and remove the completed texture reliability row from the active backlog.

Validation

  • npm test -- --run src/components/editor/textureUpload.test.ts
  • npm run test:ci
  • npm run lint
  • npm run type-check
  • npx playwright test tests/e2e/smoke.spec.ts -g "texture"
  • npm run build
  • npm run check:bundle
  • npm run check-format
  • git diff --check

Browser QA

  • Verified http://127.0.0.1:4173/ in the Codex in-app browser.
  • Confirmed the app rendered, texture storage summary was present, updated texture copy was visible, and console warnings/errors were empty.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements client-side texture optimization, increasing the maximum supported upload size to 16MB by downscaling images to a maximum dimension of 2048px and compressing them before storage. The changes include updates to the MaterialEditor component, new utility functions for image processing in textureUpload.ts, and expanded unit and E2E tests. Review feedback suggests optimizing memory usage by lazily generating original data URLs and replacing the manual base64 conversion logic with the native FileReader API.

Comment thread src/components/editor/textureUpload.ts Outdated
}

export async function prepareTextureUpload(file: File): Promise<PreparedTextureUpload> {
const originalDataUrl = await blobToDataUrl(file);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The originalDataUrl is generated eagerly for every upload. For large textures (up to 16MB), this creates a ~21MB string in memory before optimization even begins. Since optimization is likely to be used for these large files, it would be more memory-efficient to generate the original data URL lazily only if optimization fails or if the optimized version is rejected by storage limits. You can compare binary sizes (blob.size vs file.size) inside optimizeTextureFile to determine if the compressed version is smaller without needing the full original data URL string.

Comment on lines +162 to +186
async function blobToDataUrl(blob: Blob): Promise<string> {
const buffer = await blob.arrayBuffer();
const base64 = arrayBufferToBase64(buffer);
return `data:${blob.type || 'application/octet-stream'};base64,${base64}`;
}

function arrayBufferToBase64(buffer: ArrayBuffer): string {
const maybeBuffer = (
globalThis as typeof globalThis & {
Buffer?: { from: (input: ArrayBuffer) => { toString: (encoding: 'base64') => string } };
}
).Buffer;
if (maybeBuffer) return maybeBuffer.from(buffer).toString('base64');

const bytes = new Uint8Array(buffer);
const chunkSize = 0x8000;
let binary = '';
for (let offset = 0; offset < bytes.length; offset += chunkSize) {
const chunk = bytes.subarray(offset, offset + chunkSize);
for (let index = 0; index < chunk.length; index += 1) {
binary += String.fromCharCode(chunk[index]);
}
}
return btoa(binary);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using FileReader.readAsDataURL is a more efficient and idiomatic way to generate data URLs in the browser than manually converting an ArrayBuffer to base64. This approach leverages native browser implementation and avoids the overhead of manual chunking and string concatenation. Since this file already depends on browser-only APIs like canvas and createImageBitmap, FileReader is fully supported and allows you to remove the arrayBufferToBase64 helper entirely.

async function blobToDataUrl(blob: Blob): Promise<string> {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => resolve(reader.result as string);
    reader.onerror = () => reject(reader.error);
    reader.readAsDataURL(blob);
  });
}

@mikechaves
mikechaves marked this pull request as ready for review May 25, 2026 00:43
@mikechaves
mikechaves merged commit 49f95f2 into main May 25, 2026
2 checks passed
@mikechaves
mikechaves deleted the mike/texture-compression-storage branch May 25, 2026 00:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant