How to Create a 3D Image Slider in Elementor (No Pro Needed!)

Are you looking to add a stunning 3D image slider to your Elementor website? The good news is—you don’t need Elementor Pro! In this tutorial, I’ll walk you through the process step by step, so you can achieve this effect effortlessly.

Table of Contents

Are you looking to add a stunning 3D image slider to your Elementor website? The good news is—you don’t need Elementor Pro! In this tutorial, I’ll walk you through the process step by step, so you can achieve this effect effortlessly.

Step 1: Open Elementor and Add a Container

  1. Open the Elementor editor and create a new section.
  2. Add a Container and name it wrapper (You can name it as you like for better organization).
  3. Set the container to Full Width.
  4. Add some basic styling (optional).

Step 2: Add an Image Wrapper Container

  1. Add another Container inside the first one.
  2. Name it image-wrapper.
  3. Inside this container, add a Basic Gallery element.

💡 Important: Make sure to use square images (equal width and height) for the best effect.

Step 3: Adjust Image Settings

  1. Select the images you want to use in the slider.
  2. Set the image resolution to full.
  3. Turn off the Lightbox option (since we don’t need it for this effect).

Step 4: Add Custom Code

  1. Add an HTML widget below the gallery.
  2. Paste the provided custom code (You can find the code in my video description or on my website).
  3. Add a CSS class to the image-wrapper container:
    • Go to Advanced > CSS Class
    • Add the class name

Step 5: Hide the Basic Gallery

Since the animation will be controlled via code, we need to hide the default gallery:

  1. Go to Advanced > Responsive
  2. Enable Hide on All Devices

Step 6: Publish & Check the Animation

  1. Click Publish and preview your page.
  2. Your 3D slider should now be working! 🎉

Bonus: Customize the Effect

Want to tweak the animation?

  • Reverse the Slide Direction: Add reverse as a CSS class.
  • Increase the Gap Between Images: Use -gap-30 (or any number) to adjust spacing.
A table showcasing extra class names for customizing an Elementor image slider in WordPress, including options for gap, curve, reverse direction, and speed.
Are you looking to customize your Elementor image slider in WordPress without using extra plugins? Here’s a handy guide to using CSS extra class names to tweak various slider settings such as spacing, curvature, direction, and speed.

Final Thoughts

Adding a 3D slider to your Elementor website is easier than you think! With just a few tweaks, you can enhance the look of your site and make it more interactive.

If you found this tutorial helpful, share it with others who might benefit from it. Have any questions or suggestions? Drop them in the comments below!

Happy designing! 🎨✨

CSS Class

JavaScript Code

<style>
[class^='mdw-curved-slider'],
[class*=' mdw-curved-slider']{
    height: var(--min-height, 100vh);
}
[class^='mdw-curved-slider'] canvas,
[class*=' mdw-curved-slider'] canvas{
    position: absolute;
    top: 0;
    left: 0;
}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/0.160.0/three.min.js"></script>
<script>

if(!MDWNonce111){
var MDWNonce111 = true

var selector = "[class^='mdw-curved-slider'], [class*=' mdw-curved-slider']",
    scene = [],
    renderer = [],
    options = [],
    time = [],
    camera = [],
    slideAmount = [],
    currentContainerHeight = [],
    previousContainerHeight = [],
    planes = []

addEventListener('DOMContentLoaded', function(){
    
function getWidth(gap){ return 1 + gap/100 }

function getPlaneWidth(el, camera){
    var vFov = camera.fov*Math.PI/180,
    height = 2*Math.tan(vFov/2)*camera.position.z,
    aspect = el.clientWidth/el.clientHeight,
    width = height*aspect
    return el.clientWidth/width
}

function init(e = 'none'){
Array.from(document.querySelectorAll(selector)).forEach(function(el, index){
    
    if( e == 'none' ){
        currentContainerHeight[index] = previousContainerHeight[index] = el.clientHeight
    }else{
        currentContainerHeight[index] = el.clientHeight
        if( mobileHeightChage && currentContainerHeight[index] == previousContainerHeight[index] ) return
    }

    previousContainerHeight[index] = currentContainerHeight[index]
    
    var className = el.getAttribute('class'),
    classNameIndex = className.indexOf('mdw-curved-slider'),
    shortClass = className.substring(classNameIndex, className.indexOf(' ',classNameIndex)),
    values = shortClass.split('-')
    
    options[index] = {
        speed: 30,
        gap: 10,
        curve: 12,
        direction: -1
    }
    
    values.forEach(function(value, i){
        if(value=='speed' && values[i+1] && !isNaN(values[i+1])){
            options[index].speed = values[i+1] }
        if(value=='gap' && values[i+1] && !isNaN(values[i+1])){
            options[index].gap = values[i+1] }
        if(value=='curve' && values[i+1] && !isNaN(values[i+1])){
            options[index].curve = values[i+1] }
        if(value=='reverse'){ options[index].direction = 1 }
    })
    
    var images = [],
        allImages = []
        
    time[index] = 0
        
    Array.from(el.querySelectorAll('.elementor-widget-image-gallery .gallery-item')).forEach(function(el, index){
        images.push(el.querySelector('img').getAttribute('src'))
    })
    allImages = images
    slideAmount[index] = images.length

    scene[index] = new THREE.Scene()
    camera[index] = new THREE.PerspectiveCamera( 75, el.clientWidth / el.clientHeight, 0.1, 20 )
    camera[index].position.z = 2
    
    renderer[index] = new THREE.WebGLRenderer({ alpha: true, antialias: true })
    renderer[index].setSize(el.clientWidth, el.clientHeight)
    renderer[index].setPixelRatio(window.devicePixelRatio)
    
    var previousCanvas = el.querySelector('canvas')
    if(previousCanvas) { el.removeChild(previousCanvas) }
    el.appendChild(renderer[index].domElement)
    
    var geometry = new THREE.PlaneGeometry(1, 1, 20, 20),
        planeSpace = getPlaneWidth(el,camera[index])*getWidth(options[index].gap),
        ratio = Math.ceil(el.clientWidth/(planeSpace*images.length)),
        totalImage = Math.ceil(el.clientWidth/planeSpace) + 1 + images.length,
        initialOffset = Math.ceil(el.clientWidth/(2*planeSpace)-0.5)
        
    for( var i = slideAmount[index]; i < totalImage; i++ ){
        allImages.push(images[i%slideAmount[index]])
    }
    
    planes[index] = []
    
    allImages.forEach(function (image, i) {
        var loader = new THREE.TextureLoader()
        loader.load(
            image,
            function ( texture ) {
                var material = new THREE.ShaderMaterial({
                    uniforms: {
                        tex: { value: texture },
                        curve: { value: options[index].curve }
                    },
                    vertexShader: `
                        uniform float curve;
                        varying vec2 vertexUV;
                        void main(){
                            vertexUV = uv;
                            vec3 newPosition = position;
                            float distanceFromCenter = abs(modelMatrix*vec4(position, 1.0)).x;
                            newPosition.y *= 1.0 + (curve/100.0)*pow(distanceFromCenter,2.0);
                            
                            gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0);
                        }
                    `,
                    fragmentShader: `
                        uniform sampler2D tex;
                        varying vec2 vertexUV;
                        void main(){
                            gl_FragColor = texture2D(tex, vertexUV);
                        }
                    `
                })

                planes[index][i] = new THREE.Mesh( geometry, material )
                planes[index][i].position.x = -1*options[index].direction*(i-initialOffset)*getWidth(options[index].gap)
                scene[index].add( planes[index][i] )
            }
        )
    })
})
}
init()

var currentWidth,
    previousWidth = window.innerWidth,
    mobileHeightChage = false
    
function onResize(){
    currentWidth = window.innerWidth
    mobileHeightChage = currentWidth < 768 && currentWidth == previousWidth
    init('resize')
    previousWidth = currentWidth
}

window.addEventListener('resize', function(){
    onResize()
    setTimeout(onResize, 100)
})

var previousTime = 0

function animate(currentTime){
    
    var timePassed = currentTime - previousTime
    
    Array.from(document.querySelectorAll(selector)).forEach(function(el, index){
        if(Math.abs(scene[index].position.x) >= getWidth(options[index].gap)*slideAmount[index]){ time[index] = 0 }
        time[index] += options[index].direction*timePassed*0.00001
        scene[index].position.x = time[index]*options[index].speed
        renderer[index].render(scene[index], camera[index])
    })
    
    previousTime = currentTime
    requestAnimationFrame(animate)
}
requestAnimationFrame(animate)

})
}
</script>

Related Tutorials