Visualizers occupy a significant niche in modern e-commerce and are even used in offline sales. But developing your own white-label visualizer is not the fastest and easiest task.
Therefore, in this article, we will show you what fast components can be developed using the Vision API and Automation API with a value sometimes comparable to the implementation of a full-fledged visualizer.
Thanks to the wide range of Vision API tools, there are big opportunities for implementing visualization functions for various product groups in the interior: from carpets to murals and art objects on vertical surfaces and even on the ceiling.
Now, let's pay attention to the PDP (product detail page), which serves to showcase the product and helps users make a purchasing decision.
Using rich content on such pages is highly important to increase conversions. However, the generation of rich media for all your products in the catalog is not only expensive but also routine and long in most cases. As a result, filling out all the product pages with high-quality content consumes a lot of resources.
Therefore, using the generation of a dynamic component is a great idea. Below we'll show you how to implement such a component for rendering on the fly in the Shopify store. We’ll use Shopify as the main CMS system because it is the easiest system for the beginning and integration components.
Please note that for the correct operation, you need to create a PIM account and upload products.
1. Create a PIM account by the link:
2. Go to the Web tab to get a PIM token:
3. Import all the necessary data into the PIM via the PIM Admin Tool (File upload requirements, User manual for PIM admin) by going to the Product tab and clicking Import.
What do you think if you can visualize the product on the page using only 3 pictures?
It’s possible!
Below, we'll show how to visualize your product using the component and show any amount of pictures with the selected product. Here is how it looks:
1. Go to the Online Store tab and click "..." near the Customize button. Then, choose the Edit Code option:
2. Find the Section field and click the Add a new section button. Enter the name that you like, for example wizart-rendering:
3. Next, paste this code into the section:
Important note: to get the {{IMAGE_PATH_OF_INTERIOR}} and {{ROOM_UUID}} variables, you need to send a request to the Wizart team as this information isn’t public.
{{ 'section-multicolumn.css' | asset_url | stylesheet_tag }} <link rel="stylesheet" href="{{ 'component-slider.css' | asset_url }}" media="print" onload="this.media='all'"> <noscript>{{ 'component-slider.css' | asset_url | stylesheet_tag }}</noscript> {%- style -%} .section-{{ section.id }}-padding { padding-top: {{ section.settings.padding_top | times: 0.75 | round: 0 }}px; padding-bottom: {{ section.settings.padding_bottom | times: 0.75 | round: 0 }}px; } @media screen and (min-width: 750px) { .section-{{ section.id }}-padding { padding-top: {{ section.settings.padding_top }}px; padding-bottom: {{ section.settings.padding_bottom }}px; } } {%- endstyle -%} {%- liquid assign columns_mobile_int = 1 | plus: 0 assign show_mobile_slider = false if section.settings.swipe_on_mobile and section.blocks.size > columns_mobile_int assign show_mobile_slider = true endif -%} <div class="multicolumn color-{{ section.settings.color_scheme }} gradient{% unless section.settings.background_style == 'none' and settings.text_boxes_border_thickness > 0 or settings.text_boxes_shadow_opacity > 0 %} background-{{ section.settings.background_style }}{% endunless %}{% if section.settings.title == blank %} no-heading{% endif %}"> <div class="page-width section-{{ section.id }}-padding isolate"> {%- unless section.settings.title == blank -%} <div class="title-wrapper-with-link title-wrapper--self-padded-mobile title-wrapper--no-top-margin"> <h2 class="title {{ section.settings.heading_size }}"> {{ section.settings.title | escape }} </h2> {%- if section.settings.button_label != blank and show_mobile_slider -%} <a href="{{ section.settings.button_link }}" class="link underlined-link large-up-hide">{{ section.settings.button_label | escape }}</a> {%- endif -%} </div> {%- endunless -%} <slider-component class="slider-mobile-gutter"> <ul class="multicolumn-list contains-content-container grid grid--1-col-tablet-down grid--3-col-desktop" id="Slider-template--16307815416022__1659357255b029e33c" role="list"> <li id="Slide-template--16307815416022__1659357255b029e33c-1" class="multicolumn-list__item grid__item"> <div class="multicolumn-card content-container"> <div class="multicolumn-card__image-wrapper multicolumn-card__image-wrapper--full-width multicolumn-card-spacing"> <div class="media media--transparent media--adapt" style="padding-bottom: 66.66666666666666%;"> <img class="multicolumn-card__image" id="render_image-0" src="https://images.wizart.ai/images/interiors/reserved/images/{{IMAGE_PATH_OF_FIRST_INTERIOR}}" sizes="(min-width: 990px) 550px, (min-width: 750px) 550px, calc(100vw - 30px)" height="1000" width="1500" loading="lazy"> </div> </div> <div class="multicolumn-card__info"> {%- if section.blocks[0].settings.title != blank -%} <h3>{{ section.blocks[0].settings.title | escape }}</h3> {%- endif -%} {%- if section.blocks[0].settings.text != blank -%} <div class="rte">{{ section.blocks[0].settings.text }}</div> {%- endif -%} </div> </div> </li> <li id="Slide-template--16307815416022__1659357255b029e33c-2" class="multicolumn-list__item grid__item"> <div class="multicolumn-card content-container"> <div class="multicolumn-card__image-wrapper multicolumn-card__image-wrapper--full-width multicolumn-card-spacing"> <div class="media media--transparent media--adapt" style="padding-bottom: 66.66666666666666%;"> <img class="multicolumn-card__image" id="render_image-1" src="https://images.wizart.ai/images/interiors/reserved/images/{{IMAGE_PATH_OF_SECOND_INTERIOR}}" sizes="(min-width: 990px) 550px, (min-width: 750px) 550px, calc(100vw - 30px)" height="1000" width="1500" loading="lazy"> </div> </div> <div class="multicolumn-card__info"> {%- if section.blocks[1].settings.title != blank -%} <h3>{{ section.blocks[1].settings.title | escape }}</h3> {%- endif -%} {%- if section.blocks[1].settings.text != blank -%} <div class="rte">{{ section.blocks[1].settings.text }}</div> {%- endif -%} </div> </div> </li> <li id="Slide-template--16307815416022__1659357255b029e33c-3" class="multicolumn-list__item grid__item"> <div class="multicolumn-card content-container"> <div class="multicolumn-card__image-wrapper multicolumn-card__image-wrapper--full-width multicolumn-card-spacing"> <div class="media media--transparent media--adapt" style="padding-bottom: 66.66666666666666%;"> <img class="multicolumn-card__image" id="render_image-2" src="https://images.wizart.ai/images/interiors/reserved/images/{{IMAGE_PATH_OF_THIRD_INTERIOR}}" sizes="(min-width: 990px) 550px, (min-width: 750px) 550px, calc(100vw - 30px)" height="1000" width="1500" loading="lazy"> </div> </div> <div class="multicolumn-card__info"> {%- if section.blocks[2].settings.title != blank -%} <h3>{{ section.blocks[2].settings.title | escape }}</h3> {%- endif -%} {%- if section.blocks[2].settings.text != blank -%} <div class="rte">{{ section.blocks[2].settings.text }}</div> {%- endif -%} </div> </div> </li> </ul> {%- if show_mobile_slider -%} <div class="slider-buttons no-js-hidden medium-hide"> <button type="button" class="slider-button slider-button--prev" name="previous" aria-label="{{ 'general.slider.previous_slide' | t }}">{% render 'icon-caret' %} </button> <div class="slider-counter caption"> <span class="slider-counter--current">1</span> <span aria-hidden="true"> / </span> <span class="visually-hidden">{{ 'general.slider.of' | t }}</span> <span class="slider-counter--total">{{ section.blocks.size }}</span> </div> <button type="button" class="slider-button slider-button--next" name="next" aria-label="{{ 'general.slider.next_slide' | t }}">{% render 'icon-caret' %} </button> </div> {%- endif -%} </slider-component> <div style="visibility: hidden;">fgddfgfdgfgdfg</div> </div> </div> <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js" integrity="sha512-E8QSvWZ0eCLGk4km3hxSsNmGWbLtSCSUcewDQPQWZF6pEU8GlT8a5fF32wOl1i8ftdMhssTrF/OhyGWwonTcXA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> <script> document.addEventListener('DOMContentLoaded', function () { setTimeout(getProductData, 300); }) function getProductData() { jQuery.ajax({ url: 'https://pim-client.wizart.ai/api/articles/search?q={"$or":[{"$eq":["name","{{ product.selected_or_first_available_variant.sku }}"]},{"$eq":["vendor_code","{{ product.selected_or_first_available_variant.sku }}"]}]}', headers: { "Authorization": "{{PIM_TOKEN}}" }, success: function (result) { callback(result.data[0]); } }); } function callback(data) { let rooms = prepareData(data) setTimeout(sendToApply, 0, rooms[0], 0); setTimeout(sendToApply, 500, rooms[1], 1); setTimeout(sendToApply, 1000, rooms[2], 2); } function prepareData(data) { let rooms = []; let room1 = { room_data: { room: { id: "{{ROOM_UUID}}", image_path: "interiors/reserved/images/{{IMAGE_PATH_OF_FIRST_INTERIOR}}", walls: [ { custom_rotation_angle: 0, is_active: false, wall_id: 0, }, { custom_rotation_angle: 0, is_active: false, wall_id: 1, }, { custom_rotation_angle: 0, is_active: false, wall_id: 2, } ] } } } let room2 = { room_data: { room: { id: "03a9e0ea-1d04-4252-8b91-44680d84fbfc", image_path: "interiors/reserved/images/{{IMAGE_PATH_OF_SECOND_INTERIOR}}", walls: [ { custom_rotation_angle: 0, is_active: false, wall_id: 0, }, { custom_rotation_angle: 0, is_active: false, wall_id: 1, }, { custom_rotation_angle: 0, is_active: false, wall_id: 2, } ] } } } let room3 = { room_data: { room: { id: "12e883af-cced-460c-abad-7cb4ad4f9b5f", image_path: "interiors/reserved/images/{{IMAGE_PATH_OF_THIRD_INTERIOR}}", walls: [ { custom_rotation_angle: 0, is_active: false, wall_id: 0, }, ] } } } if (data.application_type[0] === 'wall') { Object.assign(room1.room_data.room, { walls: [{ custom_rotation_angle: 0, is_active: true, wall_id: 0, wallpaper: data, },{ custom_rotation_angle: 0, is_active: true, wall_id: 1, wallpaper: data, },{ custom_rotation_angle: 0, is_active: true, wall_id: 2, wallpaper: data, }] }); Object.assign(room2.room_data.room, { walls: [{ custom_rotation_angle: 0, is_active: true, wall_id: 0, wallpaper: data, },{ custom_rotation_angle: 0, is_active: true, wall_id: 1, wallpaper: data, },{ custom_rotation_angle: 0, is_active: true, wall_id: 2, wallpaper: data, }] }); Object.assign(room3.room_data.room, { walls: [{ custom_rotation_angle: 0, is_active: true, wall_id: 0, wallpaper: data, }] }); } if (data.application_type[0] === 'floor') { Object.assign(room1.room_data.room, { floor: { custom_rotation_angle: 0, x: 47.6, y: 78.3, covering: data, } }); Object.assign(room2.room_data.room, { floor: { custom_rotation_angle: 0, x: 43, y: 88.5, covering: data, } }); Object.assign(room3.room_data.room, { floor: { custom_rotation_angle: 0, x: 78.8, y: 92, covering: data, } }); } if (data.application_type[0] === 'ceiling') { Object.assign(room1.room_data.room, { covering: [{ custom_rotation_angle: 0, x: 62, y: 90, covering: data, }] }); Object.assign(room2.room_data.room, { covering: [{ custom_rotation_angle: 0, x: 62, y: 90, covering: data, }] }); Object.assign(room3.room_data.room, { covering: [{ custom_rotation_angle: 0, x: 62, y: 90, covering: data, }] }); } rooms.push(room1) rooms.push(room2) rooms.push(room3) return rooms } function sendToApply(data, index) { let json_data = JSON.stringify(data) let strHash = CryptoJS.MD5('shopify-{{YOUR_STORE_NAME}}'+json_data).toString(); jQuery.ajax({ url: 'https://rni.wizart.ai/apply/?device=thumbnail&hash='+strHash, method: 'POST', headers: { "Authorization": "{{PIM_TOKEN}}" }, accept: "*/*", contentType: "application/json;", data: json_data, success: function (result) { jQuery("#render_image-"+index).attr("src", "data:image/jpeg;base64," + result); } }); return true; } </script> {% schema %} { "name": "Wizart Multicolumn", "class": "section", "tag": "section", "settings": [ { "type": "select", "id": "heading_size", "options": [ { "value": "h2", "label": "t:sections.all.heading_size.options__1.label" }, { "value": "h1", "label": "t:sections.all.heading_size.options__2.label" }, { "value": "h0", "label": "t:sections.all.heading_size.options__3.label" } ], "default": "h1", "label": "t:sections.all.heading_size.label" }, { "type": "select", "id": "image_width", "options": [ { "value": "third", "label": "t:sections.multicolumn.settings.image_width.options__1.label" }, { "value": "half", "label": "t:sections.multicolumn.settings.image_width.options__2.label" }, { "value": "full", "label": "t:sections.multicolumn.settings.image_width.options__3.label" } ], "default": "full", "label": "t:sections.multicolumn.settings.image_width.label" }, { "type": "select", "id": "image_ratio", "options": [ { "value": "adapt", "label": "t:sections.multicolumn.settings.image_ratio.options__1.label" }, { "value": "portrait", "label": "t:sections.multicolumn.settings.image_ratio.options__2.label" }, { "value": "square", "label": "t:sections.multicolumn.settings.image_ratio.options__3.label" }, { "value": "circle", "label": "t:sections.multicolumn.settings.image_ratio.options__4.label" } ], "default": "adapt", "label": "t:sections.multicolumn.settings.image_ratio.label" }, { "type": "select", "id": "column_alignment", "options": [ { "value": "left", "label": "t:sections.multicolumn.settings.column_alignment.options__1.label" } ], "default": "left", "label": "t:sections.multicolumn.settings.column_alignment.label" }, { "type": "select", "id": "background_style", "options": [ { "value": "none", "label": "t:sections.multicolumn.settings.background_style.options__1.label" }, { "value": "primary", "label": "t:sections.multicolumn.settings.background_style.options__2.label" } ], "default": "primary", "label": "t:sections.multicolumn.settings.background_style.label" }, { "type": "text", "id": "button_label", "default": "Button label", "label": "t:sections.multicolumn.settings.button_label.label" }, { "type": "url", "id": "button_link", "label": "t:sections.multicolumn.settings.button_link.label" }, { "type": "select", "id": "color_scheme", "options": [ { "value": "accent-1", "label": "t:sections.all.colors.accent_1.label" }, { "value": "accent-2", "label": "t:sections.all.colors.accent_2.label" }, { "value": "background-1", "label": "t:sections.all.colors.background_1.label" }, { "value": "background-2", "label": "t:sections.all.colors.background_2.label" }, { "value": "inverse", "label": "t:sections.all.colors.inverse.label" } ], "default": "background-1", "label": "t:sections.all.colors.label" }, { "type": "header", "content": "t:sections.all.padding.section_padding_heading" }, { "type": "range", "id": "padding_top", "min": 0, "max": 100, "step": 4, "unit": "px", "label": "t:sections.all.padding.padding_top", "default": 36 }, { "type": "range", "id": "padding_bottom", "min": 0, "max": 100, "step": 4, "unit": "px", "label": "t:sections.all.padding.padding_bottom", "default": 36 } ], "blocks": [ { "type": "column", "name": "t:sections.multicolumn.blocks.column.name", "limit": 3, "settings": [ { "type": "text", "id": "title", "default": "Column", "label": "t:sections.multicolumn.blocks.column.settings.title.label" }, { "type": "richtext", "id": "text", "default": "<p>Pair text with an image to focus on your chosen product, collection, or blog post. Add details on availability, style, or even provide a review.</p>", "label": "t:sections.multicolumn.blocks.column.settings.text.label" } ] } ], "presets": [ { "name": "Render Examples", "blocks": [ { "type": "column" }, { "type": "column" }, { "type": "column" } ] } ] } {% endschema %}
4. Then, go back to the Online Store tab and click the Customize button:
5. Navigate to the Products template:
6. Next, click the Add section button and choose the previously created section. In our case, it’s wizart-rendering:
7. Then you will see the section displayed like this:
8. You can customize it to match your style if needed:
By following these steps, you can integrate a convenient, customizable component. This feature can improve the visualization of your products. Use any interiors you want: from our library or even your own. You need only upload them into your PIM account.
If you have any issues with the component, please, feel free to send a message to support@wizart.ai. And our wonderful sales team will always be happy to help you with unique interiors and product images.