この記事は現在英語版のみです。

(記事)

Building a Signage Preview System: Logo Upload, Photo Alignment, and a 3D Showroom

An SVG logo is turned into 3D letters inside the browser and mounted on a demo building or a storefront photo. Photos never go to a server, and the home page controls loading with KTX2 textures and shader precompilation.

Polish Design engineering team・2026-08-12

Contents

CHYIHAUR's website lets a customer see the finished sign before paying. The customer uploads an SVG logo, the system turns it into glowing 3D letters inside the browser, and the letters are mounted on the facades of four demo buildings. The customer can also upload a photo of a storefront and align the sign on the actual entrance. The showroom and the city on the home page are both React Three Fiber WebGL scenes, and the logo and the photo stay in the customer's browser.

The full process for 3D loading and memory is in Web 3D performance engineering, and this article carries only one section of it. The overall build scope is described under 3D interactive website development.

Desktop screen recording: changing the logo and style, switching demo buildings, fine-tuning position and size with sliders, then switching to photo simulation.

Showroom: Turning SVG into 3D Letters with Glow Materials

The showroom has four demo buildings, each with a model file under 140 KB. Each building has its own sign position, rotation, and scale settings, and one building has two more sign positions, on the side and on the top, in addition to the central sign; each position can take a different logo. An uploaded SVG is first checked with DOMParser, and only a file whose root node is svg and that contains at least one shape node goes into the scene.

The conversion parses paths with the three.js SVGLoader and extrudes each shape into a 3D letter with ExtrudeGeometry, dividing curves into 6 segments. Coordinates are normalized so that the longest side is 10 units and centered, so a logo of any aspect ratio enters the scene at the same size, and the size and thickness sliders take over from there. A multicolor logo keeps the fill color of each path in the file by default, and it can be switched to a single custom color. The color picker offers 14 common sign colors, a color code field, and the system color picker.

Glow comes from the self-illumination of the material. Filled faces use MeshStandardMaterial with emissive set to the same color as the fill, emissiveIntensity controls the brightness, and the painted style sets emissive to 0. In photo simulation the default metalness is 0.3 and the default roughness is 0.4. The image then goes through a Bloom post-process, with a brightness threshold of 1.0 in photo simulation and 1.22 on the demo buildings, and only pixels brighter than the threshold produce a halo.

The edge trim places one box along every segment of the SVG path and merges the boxes into a single draw call with InstancedMesh; the styles are no trim, raised, and flush. For back glow in photo simulation, the logo outline is first drawn into a texture, and a custom blur shader (16 directions, 4 sampling rings) paints that texture onto a plane behind the letters, where additive blending layers the spilled light. The demo buildings put an extruded copy of the letters behind the originals instead, also with additive blending.

The sign facade in the showroom, with the style panel expanded.
The sign facade in the showroom, with the style panel expanded.

There are five styles: front glow, back glow, front-and-back glow, painted 3D letters, and advertising light box. One click on the panel switches the style, and the light box style is used on two of the demo buildings.

Letting customers do the work themselves has research behind it. The abstract of Franke et al., 2010 reports a significantly higher willingness to pay for products customers design themselves, independent of preference fit and design effort, with a sense of accomplishment mediating the effect. The study covers mass customization products and does not address signage, so only the direction is taken from it here.

The same screen works for proposals. Pressing Present collapses the bottom menu and the top navigation bar and hides both with visibility, so keyboard Tab does not stop on controls that cannot be seen. The camera on a demo building then swings left and right along a sine curve, with an amplitude of 72 or 45 degrees depending on the building and a period of about 52 seconds. When the system is set to reduce motion, the camera does not swing automatically.

A person sits cross-legged with an open laptop on the lap, looking down at the screen.

On the first visit to the showroom, a seven-step guide appears after about 2.6 seconds, and each step comes with a screen showing the actual operation. Esc closes the guide, the left and right arrow keys turn the pages, and focus stays inside the dialog; the How to use button in the upper right brings it back at any time. The upload window has the same focus management, and when it closes, focus returns to the button that opened it.

The guide on first entry to the showroom, explaining each operation step by step.

Photo Simulation: Aligning the Sign to the Customer's Storefront

Photo simulation treats the storefront photo as a background plane that fills the window, and the sign is a 3D object placed in front of it; both are composited in the same WebGL scene. The background plane fills the window with cover scaling based on the aspect ratio of the photo, uses a material that lighting does not affect, and has its render order fixed at the very bottom. The sign responds to scene lighting and Bloom, gets a white outline from an Outline post-process when the pointer passes over it, and shows draggable control axes after a click.

Nine built-in storefront scenes cover both day and night, and each carries a preset position, size, and rotation; photos taken at an angle also carry vertical and horizontal tilt angles. When the background changes, the photo fades out in 0.35 seconds and fades in over 0.45 seconds, and the preset values of the scene apply at the moment the fade-out ends and the new photo goes in. The position range is a quarter of the window width and height, and a sign that goes beyond it is pulled back automatically. During a drag, position and rotation update once per movement, and the rotation sliders cover plus or minus 90 degrees on each of the vertical, horizontal, and depth axes.

When a photo is uploaded, the browser reads the file with URL.createObjectURL, shrinks it on a canvas to a longest side of 2000 px, and outputs it as JPEG at quality 0.85 for use as the texture source; the upload window accepts JPEG and PNG. A 4000 x 3000 photo straight from a phone becomes 2000 x 1500, and the decompressed texture drops from about 48 MB to about 12 MB (width, height, and 4 bytes per pixel multiplied together, mipmaps not counted). The photo is processed only in the browser and is never sent to a server.

Fig. 1Logos and storefront photos are processed only in the browser, and the server supplies static files

User's browser

  • Read the SVG and photosFileReader, canvas downscaling
  • Convert to 3D letters and compositeWebGL scene with Bloom post-processing
Models, textures, decoders

Development team's server

  • Static filesBuilding models, KTX2 textures, decoders, environment light textures

Never sentThe SVG logo and the storefront photo uploaded by the customer

Data flow of the showroom and photo simulation on the CHYIHAUR website.

Mobile screen recording: switching logo templates and styles, and adjusting position and rotation with sliders.

The same operations work on a phone. The control panel has a maximum height of 260 px and scrolls inside the panel when the content is longer, with the system safe area kept at the bottom edge. The canvas height uses svh units, a single finger pans, and two fingers pinch to zoom.

3D City Home Page: Loading Order and Camera Control

The home page is a 3D city that can be dragged and zoomed, with six hotspots linking to the consultation, cases, about, services, showroom, and ESG pages. The whole 3D scene is deferred with next/dynamic and server-side rendering is turned off, so three.js, drei, and the city model stay out of the initial load of the first screen, and hydration and first paint of the text content below are unaffected.

Mobile home page after the 3D city has loaded.
Mobile home page after the 3D city has loaded.

The camera controller is custom-written and replaces the CameraControls built into drei. Zoom moves along the line of sight, with the distance limited to between 10 and 30 units and panning limited to plus or minus 12 units, and input is smoothed exponentially so that it does not depend on the frame rate. The mouse wheel zooms only while Ctrl or Command is held, and ordinary scrolling is left to the page. The canvas is set to touch-action: pan-y, so on a phone a vertical swipe still scrolls the page, and only horizontal drags and two-finger pinches go to the 3D scene. Hotspot labels appear only when the camera distance is within 25 units.

The entry sequence runs in this order:

  1. Preload first finishes compiling the materials in the scene and uploads the textures to the GPU.
  2. gl.compileAsync compiles all shaders in the background, and the scene is marked ready only after that, with a wait of 3 seconds at most.
  3. Once the scene is ready, the blur pass in EffectComposer goes from blurred to sharp in 1.2 seconds, and the whole pass is unmounted at the end, so no full-screen post-process stays resident.
  4. When the blur ends, the navigation bar and tagline fade in, and the camera moves closer along the line of sight over 1.5 seconds.

The WebGL recommendations from MDN also list compiling all shaders before linking as a practice, and KHR_parallel_shader_compile can be used to avoid blocking. The render dpr is capped at 1.5. Once the first screen scrolls out of the window, an IntersectionObserver switches frameloop to never, and the city stops drawing.

Textures and Geometry: KTX2, Draco, and Self-Hosted Decoders

The city scene has 34 meshes and 25 looping animations. The geometry is compressed with Draco, and the GLB file is about 2.7 MB; the Khronos Draco extension specification defines how this compression is written in glTF. All meshes share nine MeshBasicMaterial instances with no real-time lighting, and the materials are disposed one by one when the visitor leaves the home page.

The textures were originally eight 4K images; the scene now uses 2K versions encoded as KTX2 (ETC1S), which keeps the textures in a compressed format in GPU memory. The estimate falls from about 681 MB to about 30 MB. The KHR_texture_basisu specification explains that for textures in a KTX2 container with Basis Universal supercompression, the engine has to transcode such textures into a block-compressed format that the platform supports. The specification describes only the transcoding mechanism and gives no savings figure, so the 30 MB is an estimate for this scene.

The Draco and Basis decoders, along with the six sets of environment light textures used by the showroom, are all hosted on the development team's own servers. If an environment preset has no matching local file, no environment texture is mounted, and the page does not reach out to a third-party URL. The before-and-after comparison and how it is calculated are in 3D model optimization before and after, and how much a phone can hold is in memory budgets for 3D on phones.

What Happens When Loading Fails

Before the 3D scene is mounted, the page probes WebGL by trying webgl2 and then webgl and confirming that the context is not in a lost state. If neither is available, the page switches directly to a static cover and shows the navigation bar. The probe runs only after mounting, and the first render is always treated as available, which matches the server output and avoids a hydration mismatch. An exception thrown while the scene renders is caught by an error boundary, and the page falls back to the static cover in the same way. The cover consists of the brand logo, a one-line statement, and a note, and content below the first screen is unaffected.

The loading progress layer of the showroom shows a percentage with useProgress from drei. When all resources are already in the cache, the layer closes automatically after a grace period of about 1.5 seconds; when loading fails, it closes immediately, and a separate cap of 20 seconds applies. When the visitor leaves the showroom, the GLB and textures specific to that scene are released from the cache, and the shared environment light textures are kept for the next showroom.

Fig. 2When WebGL is unavailable or scene rendering fails, the home page shows a static cover and the content below loads as usual
  1. Enter the home pageAfter mounting, probe webgl2 and webgl and confirm the context is not lost
  2. Load the city sceneTexture upload and shader precompilation, 3 seconds at most
  • Scene ready
    Interface enters, and the city can be dragged and zoomed
  • WebGL unavailable, or an exception thrown during rendering
    Show the static cover and the navigation barContent below the first screen loads as usual

Loading flow of the 3D city on the CHYIHAUR home page.

From Preview to Consultation: Sales Proposals and the Form

The showroom can be reached from the home page. A customer swaps in a logo, aligns it to the storefront, and sees the finished sign before making an inquiry, and a salesperson can open the same screen in a meeting to explain a proposal.

Fig. 3A customer moves from the 3D city to the inquiry form, and sales can use the showroom screen for proposals
See
Swap
Align
Inquire
Customer
Sales
1
CustomerDrag and zoom the 3D citySix hotspots link to the showroom and the consultation
2
CustomerUpload an SVG logo and change the styleAny of four demo buildings
3
CustomerUpload a storefront photo and drag to alignThe photo is processed only in the browser
4
CustomerSubmit the inquiry formAsks for the items needed and a budget range first
5
SalesPress Present, and the controls collapseThe camera on a demo building swings automatically, so a proposal runs directly on a laptop

Usage path on the CHYIHAUR website.

Every case page ends with an embedded inquiry form that first asks for the items needed and a budget range.

The inquiry form at the end of a case page, asking for the items needed and a budget range first.
The inquiry form at the end of a case page, asking for the items needed and a budget range first.

The website currently comes in Traditional Chinese, English, Japanese, and Malay. The showroom content lives inside a canvas, so the page also carries an H1 and descriptive paragraphs that only crawlers and screen readers read, and it outputs CollectionPage and BreadcrumbList structured data. Case pages output FAQ structured data and llms.txt. Google's JavaScript SEO documentation explains that content visible after rendering is what gets indexed, that rendering runs when resources allow, and that not every bot executes JavaScript, so it recommends server-side rendering or pre-rendering. The full approach is in search and AI crawler readability for 3D websites.

Product Types This Fits

The same approach fits industries where the finished product is visible to the customer only after it is made: signage, building materials, doors, windows and curtain walls, interior renovation, exhibition booths, and lighting. What these share is made-to-order work, where the product does not yet exist when the customer pays. A different approach for made-to-order services is described in online proposals for wedding banquet catering.

When It Does Not Fit

  • Standard products. The model and appearance are fixed, and customers can decide from a catalog.
  • Projects where the decision depends mainly on price or delivery time.
  • Projects with few orders where each one needs an on-site measurement before it can be settled, since the cost of building and maintaining the preview is harder to recover.

What to Prepare Before Starting

  • The decision customers most often get stuck on before paying
  • The product items and style types to preview
  • Existing logos (SVG preferred) and storefront photos
  • The devices sales will use for proposals

Once these are ready, discuss the planning of the showroom and previews.

(お問い合わせ)

Start a project.

三つのステップで、判断に足る進め方と予算帯が得られます。

  1. 01最初の打ち合わせを予約:時間を選びます。完全な仕様は不要です。
  2. 02オンライン相談(15〜30分):進め方の方向性、予算帯、スケジュール案を提示。
  3. 03素材を確認したうえで、約3〜5営業日で見積書兼納品仕様明細書を提出します。
メール
hello@polish-design.com.tw
郵送先住所
(104) 台湾 台北市 中山区 植福路191-1号 3F

最初の打ち合わせを予約

平日 16:00〜18:00オンライン30分

先に時間帯を選び、Google アカウントまたはメールアドレスで確認します。予約が確定するとカレンダーに追加できます。

時間を選ぶ

または、先に内容を送信

まだ時間を決めていない場合は、先にプロジェクトの内容をお送りください。担当者がメールで返信します。送信後に続けて予約することもできます。

補足情報(任意):サービス項目、現在のフェーズ、既存リンク、知ったきっかけ

入力内容は、今回のプロジェクト評価にのみ使用します。

ISO/IEC 27001:2022 認証書

(Certificate)

ISO/IEC 27001:2022

認証機関
ARES International Certification
認証範囲
インタラクティブ没入型3Dシステムの開発プロセス、ならびにネットワークおよびインフラの運用・管理。

認証機関サイトで確認