<label class="switch">
    <input type="checkbox">
    <div>
        <canvas></canvas>
    </div>
    <span>Switch</span>
</label>

<label class="switch">
    <input type="checkbox" checked>
    <div>
        <canvas></canvas>
    </div>
    <span>Switch</span>
</label>

<!-- dribbble -->
<a class="dribbble" href="https://dribbble.com/shots/7063524-3D-Switch-animation" target="_blank"><img src="https://cdn.dribbble.com/assets/dribbble-ball-mark-2bd45f09c2fb58dbbfb44766d5d1d07c5a12972d602ef8b32204d28fa3dda554.svg" alt=""></a>
.switch {
    --background: #E4ECFA;
    --background-active: #275EFE;
    --shadow: rgba(18, 22, 33, .04);
    --shadow-dark: rgba(18, 22, 33, .32);
    cursor: pointer;
    display: flex;
    -webkit-tap-highlight-color: transparent;
    //zoom for demo
    zoom: 1.5;
    input[type="checkbox"] {
        display: none;
        & + div {
            width: 40px;
            height: 24px;
            border-radius: 12px;
            overflow: hidden;
            position: relative;
            transition: transform .3s ease;
            background: var(--background);
            -webkit-mask-image: -webkit-radial-gradient(white, black);
            &:before,
            canvas {
                left: 0;
                top: 0;
                display: block;
                position: absolute;
                transition: transform .5s ease, filter .5s ease;
            }
            &:before {
                --x: -100%;
                --s: 1;
                content: '';
                width: 24px;
                height: 24px;
                border-radius: 50%;
                background: var(--background-active);
                transform: translateX(var(--x)) scale(var(--s));
            }
            canvas {
                --x: -8px;
                display: block;
                filter: drop-shadow(0 1px 2px var(--shadow));
                transform: translate(var(--x), -8px);
            }
        }
        &:checked {
            & + div {
                &:before {
                    --s: 3;
                    --x: 0;
                }
                canvas {
                    --x: 8px;
                    filter: drop-shadow(0 1px 2px var(--shadow-dark));
                }
            }
        }
    }
    &:active {
        input[type="checkbox"] {
            & + div {
                transform: scale(.92);
            }
        }
    }
    span {
        line-height: 24px;
        font-size: 14px;
        font-weight: 500;
        display: block;
        margin: 0 0 0 8px;
    }
}

html {
    box-sizing: border-box;
    -webkit-font-smoothing: antialiased;
}

* {
    box-sizing: inherit;
    &:before,
    &:after {
        box-sizing: inherit;
    }
}

// Center & dribbble
body {
    min-height: 100vh;
    display: flex;
    justify-content: center;
    align-items: center;
    flex-direction: column;
    background: #fff;
    font-family: 'Roboto', Arial;
    color: #6C7486;
    .switch {
        &:not(:last-child) {
            margin-bottom: 16px;
        }
    }
    .dribbble {
        position: fixed;
        display: block;
        right: 20px;
        bottom: 20px;
        img {
            display: block;
            height: 28px;
        }
    }
}
View Compiled
$('.switch').each(function() {

    let toggle = $(this),
        input = toggle.children('input'),
        $canvas = toggle.find('canvas'),
        canvas = $canvas[0],
        renderer = new THREE.WebGLRenderer({
            canvas: canvas,
            context: canvas.getContext('webgl2'),
            antialias: true,
            alpha: true
        });

    renderer.setSize(40, 40);
    renderer.setPixelRatio(window.devicePixelRatio ? window.devicePixelRatio * 2 : 2);

    renderer.shadowMap.enabled = true;

    let scene = new THREE.Scene();
        camera = new THREE.PerspectiveCamera(45, $canvas.width() / $canvas.height(), 0.1, 1000);

    camera.position.z = 92;

    let shape = new THREE.CylinderGeometry(16, 16, 8, 20);
    let material = new THREE.MeshPhongMaterial({
        color: 0xE4ECFA,
        shininess: 20,
        opacity: .96,
        transparent: true
    });
    let donut = new THREE.Mesh(shape, material);

    scene.add(donut);

    let lightTop = new THREE.DirectionalLight(0xFFFFFF, .6);
    lightTop.position.set(0, 60, 60);
    lightTop.castShadow = true;
    scene.add(lightTop);

    let right = new THREE.DirectionalLight(0xFFFFFF, .5);
    right.position.set(20, 20, 40);
    right.castShadow = true;
    scene.add(right);

    let left = new THREE.DirectionalLight(0xFFFFFF, .5);
    left.position.set(-20, 20, 40);
    left.castShadow = true;
    scene.add(left);

    let active = new THREE.DirectionalLight(0x275EFE, .8);
    active.position.set(0, -80, 20);
    active.castShadow = true;
    scene.add(active);

    scene.add(new THREE.AmbientLight(0x6C7486));

    var render = function() {

        requestAnimationFrame(render);

        TweenMax.render();

        renderer.render(scene, camera);

    };

    render();

    input.on('change', e => {
        let checked = input.is(':checked'),
            z = !checked ? THREE.Math.degToRad(0) : THREE.Math.degToRad(90),
            x = !checked ? THREE.Math.degToRad(90) : THREE.Math.degToRad(0);
        TweenMax.to(donut.rotation, 3, {
            ease: Elastic.easeOut.config(!checked ? 1.16 : 1.04, !checked ? .32 : .36),
            z: z,
            x: x
        });
        active.intensity = !checked ? .4 : .8;
        active.color.setHex(!checked ? 0xFFFFFF : 0x275EFE);
    }).trigger('change');

});
Run Pen

External CSS

  1. https://fonts.googleapis.com/css?family=Roboto:400,500,700&amp;display=swap

External JavaScript

  1. https://code.jquery.com/jquery-3.4.1.min.js
  2. https://cdnjs.cloudflare.com/ajax/libs/three.js/107/three.min.js
  3. https://cdnjs.cloudflare.com/ajax/libs/gsap/2.1.3/TweenMax.min.js