index.html:
<!DOCTYPE html>
<html lang="ar">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>محادثة الذكاء الاصطناعي</title>
    <link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
    <style>
        @import url('https://fonts.googleapis.com/css2?family=Amiri:wght@400;700&display=swap');
        body { font-family: 'Amiri', serif; background-color: #1a1a1a; }
        /* أنماط المرئي الدائري */
        #visualizerContainer {
            width: 100px;
            height: 100px;
            margin: 0 auto;
            position: relative;
        }
        #visualizer {
            width: 100%;
            height: 100%;
            background: transparent;
        }
        .circle {
            width: 100%;
            height: 100%;
            border: 4px solid #38bdf8;
            border-radius: 50%;
            animation: pulse 1.5s infinite;
            position: absolute;
            top: 0;
            left: 0;
            box-shadow: 0 0 10px #38bdf8;
        }
        @keyframes pulse {
            0% {
                transform: scale(0.75);
                opacity: 1;
            }
            100% {
                transform: scale(1.25);
                opacity: 0;
            }
        }
        .hidden {
            display: none;
        }
        /* أنماط الرسائل */
        .user-message {
            background-color: #2563eb;
            color: white;
            padding: 10px 15px;
            border-radius: 20px 20px 0px 20px;
            display: inline-block;
            max-width: 80%;
            margin-bottom: 10px;
        }
        .ai-message {
            background-color: #374151;
            color: white;
            padding: 10px 15px;
            border-radius: 20px 20px 20px 0px;
            display: inline-block;
            max-width: 80%;
            margin-bottom: 10px;
        }
    </style>
</head>
<body class="text-white flex flex-col items-center justify-center min-h-screen">
    <h1 class="text-4xl mb-8">محادثة الذكاء الاصطناعي</h1>

    <!-- مرئي التحدث -->
    <div id="visualizerContainer" class="mb-8">
        <canvas id="visualizer"></canvas>
        <div id="circleVisualizer" class="circle hidden"></div>
    </div>

    <!-- التحكم في المحادثة -->
    <div class="w-2/3 flex flex-col items-center">
        <button id="talkButton" onclick="toggleChat()" 
                class="bg-green-500 px-6 py-3 mt-6 rounded hover:bg-green-700 w-full">
            🎤 ابدأ المحادثة
        </button>
        <button id="stopButton" onclick="stopRecognition()" 
                class="bg-red-500 px-6 py-3 mt-4 rounded hover:bg-red-700 w-full hidden">
            ⛔ إيقاف المحادثة
        </button>
    </div>

    <!-- سجل المحادثة -->
    <div id="historyContainer" class="mt-8 p-4 bg-gray-800 rounded w-2/3 overflow-auto max-h-96">
        <!-- ستتم إضافة الرسائل هنا -->
    </div>

    <script src="/static/script.js" defer></script>
</body>
</html>


script.js:
const historyContainer = document.getElementById('historyContainer');
const talkButton = document.getElementById('talkButton');
const stopButton = document.getElementById('stopButton');
const circleVisualizer = document.getElementById('circleVisualizer');

let recognition;
let isListening = false;

// سجل المحادثة
let conversationHistory = [];

// تحميل سجل المحادثة من Local Storage
function loadConversationHistory() {
    const history = localStorage.getItem('conversationHistory');
    if (history) {
        conversationHistory = JSON.parse(history);
        displayConversationHistory();
    }
}

// حفظ سجل المحادثة إلى Local Storage
function saveConversationHistory() {
    localStorage.setItem('conversationHistory', JSON.stringify(conversationHistory));
}

// عرض سجل المحادثة
function displayConversationHistory() {
    historyContainer.innerHTML = '';
    conversationHistory.forEach(item => {
        const messageDiv = document.createElement('div');
        messageDiv.className = 'mb-4';

        // رسالة المستخدم
        const userMessageDiv = document.createElement('div');
        userMessageDiv.className = 'text-right';
        const userMessageSpan = document.createElement('span');
        userMessageSpan.className = 'user-message';
        userMessageSpan.textContent = item.user;
        userMessageDiv.appendChild(userMessageSpan);

        // رسالة الذكاء الاصطناعي
        const aiMessageDiv = document.createElement('div');
        aiMessageDiv.className = 'text-left';
        const aiMessageSpan = document.createElement('span');
        aiMessageSpan.className = 'ai-message';
        aiMessageSpan.textContent = item.ai;
        aiMessageDiv.appendChild(aiMessageSpan);

        messageDiv.appendChild(userMessageDiv);
        messageDiv.appendChild(aiMessageDiv);
        historyContainer.appendChild(messageDiv);
    });
    // التمرير إلى الأسفل
    historyContainer.scrollTop = historyContainer.scrollHeight;
}

// إعداد Web Speech API
function initRecognition() {
    recognition = new (window.SpeechRecognition || window.webkitSpeechRecognition)();
    recognition.lang = 'ar-SA';
    recognition.interimResults = false;
    recognition.maxAlternatives = 1;

    recognition.onstart = function() {
        circleVisualizer.classList.remove('hidden');
    };

    recognition.onend = function() {
        circleVisualizer.classList.add('hidden');
    };

    recognition.onresult = async function(event) {
        const transcript = event.results[0][0].transcript;
        addMessageToHistory('user', transcript);
        await sendMessage(transcript);

        if (isListening) {
            recognition.start();
        }
    };

    recognition.onerror = function(event) {
        console.error('خطأ في التعرف على الكلام:', event.error);
        if (isListening) {
            recognition.start();
        }
    };
}

// بدء المحادثة
async function toggleChat() {
    isListening = true;
    talkButton.classList.add('hidden');
    stopButton.classList.remove('hidden');

    if (!recognition) initRecognition();
    recognition.start();
}

// إيقاف المحادثة
function stopRecognition() {
    isListening = false;
    recognition.stop();
    talkButton.classList.remove('hidden');
    stopButton.classList.add('hidden');
}

// إرسال الرسالة إلى الخادم والحصول على الرد
async function sendMessage(message) {
    try {
        const response = await fetch('/chat', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ message: message })
        });

        const result = await response.json();
        addMessageToHistory('ai', result.response);

        // تشغيل الرد باستخدام تحويل النص إلى كلام
        await playAudio(result.response);
    } catch (error) {
        console.error('خطأ في إرسال الرسالة:', error);
    }
}

// إضافة الرسائل إلى السجل
function addMessageToHistory(sender, message) {
    if (sender === 'user') {
        conversationHistory.push({ user: message, ai: '' });
    } else if (sender === 'ai') {
        conversationHistory[conversationHistory.length - 1].ai = message;
    }
    displayConversationHistory();
    saveConversationHistory();
}

// تحويل النص إلى كلام وتشغيله
async function playAudio(text) {
    try {
        const response = await fetch('/text-to-speech', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ text: text })
        });

        const audioBlob = await response.blob();
        const audioUrl = URL.createObjectURL(audioBlob);
        const audio = new Audio(audioUrl);

        audio.onplay = function() {
            circleVisualizer.classList.remove('hidden');
        };

        audio.onended = function() {
            circleVisualizer.classList.add('hidden');
            if (isListening) {
                recognition.start();
            }
        };

        await audio.play();
    } catch (error) {
        console.error('خطأ في تشغيل الصوت:', error);
    }
}

// تحميل سجل المحادثة عند تحميل الصفحة
loadConversationHistory();


improve this code make it more modern and make circle visualizer visualize with audio more modern, use three.js for audio visualizer
ok chat show with button slide button under the website and also use daisyui 
use     <script src="https://cdn.tailwindcss.com"></script>
<script src="
https://cdn.jsdelivr.net/npm/daisyui@4.12.14/src/index.min.js
"></script>
<link href="
https://cdn.jsdelivr.net/npm/daisyui@4.12.14/dist/full.min.css
" rel="stylesheet">
https://cdnjs.cloudflare.com/ajax/libs/three.js/0.170.0/three.module.min.js


use this 
visualze.js
import * as THREE from 'three';
import {GUI} from 'dat.gui';
import {EffectComposer} from 'three/examples/jsm/postprocessing/EffectComposer';
import {RenderPass} from 'three/examples/jsm/postprocessing/RenderPass';
import {UnrealBloomPass} from 'three/examples/jsm/postprocessing/UnrealBloomPass';
import {OutputPass} from 'three/examples/jsm/postprocessing/OutputPass';

const renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
//renderer.setClearColor(0x222222);

const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
	45,
	window.innerWidth / window.innerHeight,
	0.1,
	1000
);

const params = {
	red: 1.0,
	green: 1.0,
	blue: 1.0,
	threshold: 0.5,
	strength: 0.5,
	radius: 0.8
}

renderer.outputColorSpace = THREE.SRGBColorSpace;

const renderScene = new RenderPass(scene, camera);

const bloomPass = new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight));
bloomPass.threshold = params.threshold;
bloomPass.strength = params.strength;
bloomPass.radius = params.radius;

const bloomComposer = new EffectComposer(renderer);
bloomComposer.addPass(renderScene);
bloomComposer.addPass(bloomPass);

const outputPass = new OutputPass();
bloomComposer.addPass(outputPass);

camera.position.set(0, -2, 14);
camera.lookAt(0, 0, 0);

const uniforms = {
	u_time: {type: 'f', value: 0.0},
	u_frequency: {type: 'f', value: 0.0},
	u_red: {type: 'f', value: 1.0},
	u_green: {type: 'f', value: 1.0},
	u_blue: {type: 'f', value: 1.0}
}

const mat = new THREE.ShaderMaterial({
	uniforms,
	vertexShader: document.getElementById('vertexshader').textContent,
	fragmentShader: document.getElementById('fragmentshader').textContent
});

const geo = new THREE.IcosahedronGeometry(4, 30 );
const mesh = new THREE.Mesh(geo, mat);
scene.add(mesh);
mesh.material.wireframe = true;

const listener = new THREE.AudioListener();
camera.add(listener);

const sound = new THREE.Audio(listener);

const audioLoader = new THREE.AudioLoader();
audioLoader.load('./assets/Beats.mp3', function(buffer) {
	sound.setBuffer(buffer);
	window.addEventListener('click', function() {
		sound.play();
	});
});

const analyser = new THREE.AudioAnalyser(sound, 32);

const gui = new GUI();

const colorsFolder = gui.addFolder('Colors');
colorsFolder.add(params, 'red', 0, 1).onChange(function(value) {
	uniforms.u_red.value = Number(value);
});
colorsFolder.add(params, 'green', 0, 1).onChange(function(value) {
	uniforms.u_green.value = Number(value);
});
colorsFolder.add(params, 'blue', 0, 1).onChange(function(value) {
	uniforms.u_blue.value = Number(value);
});

const bloomFolder = gui.addFolder('Bloom');
bloomFolder.add(params, 'threshold', 0, 1).onChange(function(value) {
	bloomPass.threshold = Number(value);
});
bloomFolder.add(params, 'strength', 0, 3).onChange(function(value) {
	bloomPass.strength = Number(value);
});
bloomFolder.add(params, 'radius', 0, 1).onChange(function(value) {
	bloomPass.radius = Number(value);
});

let mouseX = 0;
let mouseY = 0;
document.addEventListener('mousemove', function(e) {
	let windowHalfX = window.innerWidth / 2;
	let windowHalfY = window.innerHeight / 2;
	mouseX = (e.clientX - windowHalfX) / 100;
	mouseY = (e.clientY - windowHalfY) / 100;
});

const clock = new THREE.Clock();
function animate() {
	camera.position.x += (mouseX - camera.position.x) * .05;
	camera.position.y += (-mouseY - camera.position.y) * 0.5;
	camera.lookAt(scene.position);
	uniforms.u_time.value = clock.getElapsedTime();
	uniforms.u_frequency.value = analyser.getAverageFrequency();
    bloomComposer.render();
	requestAnimationFrame(animate);
}
animate();

window.addEventListener('resize', function() {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
	bloomComposer.setSize(window.innerWidth, window.innerHeight);
});

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Perlin Noise X Vertex Shader</title>
    <style>
        body {
            margin: 0;
        }
    </style>
</head>
<body>
    <script id="vertexshader" type="vertex">
      uniform float u_time;

      vec3 mod289(vec3 x)
      {
        return x - floor(x * (1.0 / 289.0)) * 289.0;
      }
      
      vec4 mod289(vec4 x)
      {
        return x - floor(x * (1.0 / 289.0)) * 289.0;
      }
      
      vec4 permute(vec4 x)
      {
        return mod289(((x*34.0)+10.0)*x);
      }
      
      vec4 taylorInvSqrt(vec4 r)
      {
        return 1.79284291400159 - 0.85373472095314 * r;
      }
      
      vec3 fade(vec3 t) {
        return t*t*t*(t*(t*6.0-15.0)+10.0);
      }

      // Classic Perlin noise, periodic variant
      float pnoise(vec3 P, vec3 rep)
      {
        vec3 Pi0 = mod(floor(P), rep); // Integer part, modulo period
        vec3 Pi1 = mod(Pi0 + vec3(1.0), rep); // Integer part + 1, mod period
        Pi0 = mod289(Pi0);
        Pi1 = mod289(Pi1);
        vec3 Pf0 = fract(P); // Fractional part for interpolation
        vec3 Pf1 = Pf0 - vec3(1.0); // Fractional part - 1.0
        vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x);
        vec4 iy = vec4(Pi0.yy, Pi1.yy);
        vec4 iz0 = Pi0.zzzz;
        vec4 iz1 = Pi1.zzzz;

        vec4 ixy = permute(permute(ix) + iy);
        vec4 ixy0 = permute(ixy + iz0);
        vec4 ixy1 = permute(ixy + iz1);

        vec4 gx0 = ixy0 * (1.0 / 7.0);
        vec4 gy0 = fract(floor(gx0) * (1.0 / 7.0)) - 0.5;
        gx0 = fract(gx0);
        vec4 gz0 = vec4(0.5) - abs(gx0) - abs(gy0);
        vec4 sz0 = step(gz0, vec4(0.0));
        gx0 -= sz0 * (step(0.0, gx0) - 0.5);
        gy0 -= sz0 * (step(0.0, gy0) - 0.5);

        vec4 gx1 = ixy1 * (1.0 / 7.0);
        vec4 gy1 = fract(floor(gx1) * (1.0 / 7.0)) - 0.5;
        gx1 = fract(gx1);
        vec4 gz1 = vec4(0.5) - abs(gx1) - abs(gy1);
        vec4 sz1 = step(gz1, vec4(0.0));
        gx1 -= sz1 * (step(0.0, gx1) - 0.5);
        gy1 -= sz1 * (step(0.0, gy1) - 0.5);

        vec3 g000 = vec3(gx0.x,gy0.x,gz0.x);
        vec3 g100 = vec3(gx0.y,gy0.y,gz0.y);
        vec3 g010 = vec3(gx0.z,gy0.z,gz0.z);
        vec3 g110 = vec3(gx0.w,gy0.w,gz0.w);
        vec3 g001 = vec3(gx1.x,gy1.x,gz1.x);
        vec3 g101 = vec3(gx1.y,gy1.y,gz1.y);
        vec3 g011 = vec3(gx1.z,gy1.z,gz1.z);
        vec3 g111 = vec3(gx1.w,gy1.w,gz1.w);

        vec4 norm0 = taylorInvSqrt(vec4(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110)));
        g000 *= norm0.x;
        g010 *= norm0.y;
        g100 *= norm0.z;
        g110 *= norm0.w;
        vec4 norm1 = taylorInvSqrt(vec4(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111)));
        g001 *= norm1.x;
        g011 *= norm1.y;
        g101 *= norm1.z;
        g111 *= norm1.w;

        float n000 = dot(g000, Pf0);
        float n100 = dot(g100, vec3(Pf1.x, Pf0.yz));
        float n010 = dot(g010, vec3(Pf0.x, Pf1.y, Pf0.z));
        float n110 = dot(g110, vec3(Pf1.xy, Pf0.z));
        float n001 = dot(g001, vec3(Pf0.xy, Pf1.z));
        float n101 = dot(g101, vec3(Pf1.x, Pf0.y, Pf1.z));
        float n011 = dot(g011, vec3(Pf0.x, Pf1.yz));
        float n111 = dot(g111, Pf1);

        vec3 fade_xyz = fade(Pf0);
        vec4 n_z = mix(vec4(n000, n100, n010, n110), vec4(n001, n101, n011, n111), fade_xyz.z);
        vec2 n_yz = mix(n_z.xy, n_z.zw, fade_xyz.y);
        float n_xyz = mix(n_yz.x, n_yz.y, fade_xyz.x); 
        return 2.2 * n_xyz;
      }

      uniform float u_frequency;

      void main() {
          float noise = 3.0 * pnoise(position + u_time, vec3(10.0));
          float displacement = (u_frequency / 30.) * (noise / 10.);
          vec3 newPosition = position + normal * displacement;
          gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0);
      }
    </script>
    <script id="fragmentshader" type="fragment">
        uniform float u_red;
        uniform float u_blue;
        uniform float u_green;
        void main() {
            gl_FragColor = vec4(vec3(u_red, u_green, u_blue), 1. );
        }
    </script>
    <script src="./js/visualze.js" type="module"></script>
</body>
</html>