Може когось роздуплить))

in Ukraine on Steem9 days ago

duck-ai-image-2026-07-06-19-59...
duck.ai


У ході копирсання й штурхання чогось десь у тирнеті)) Трапилась цікава інформація, не просто цікава, а корисна, психологічно роздупляюча й терапевтично придатна. І при цьому доступна будь кому. Мова піде про звуки. Звуки не прості, а супровідні та фонові. Дістати їх можна за один підхід і будь якого шурхотячого ШІ. Це буде корисно для творчих людей та всіх хто любить або часто клацає по клавіатурі фізичній чи віртуальній.

Почну здалеку, декий софт для подібного є. Він чи платний, чи в демо режимі обмежений набір функцій, або ще шось що схоже але дещо не те, або не але не зовсім. Мною були поробувани OmmWriter чи якось так, де є звук фоновий і звуки натиснень клавіатури, якась друкарська машинка і ще якісь (але то платна версія або десктоп версія). Іще шось онлай теж подібного штибу, що моджна було поклацать онлайн без реєстрації. І воно мене не задовольнило, в плані звуків.

Якщо вірить ШІ і тому маленькому досвіду, поклацувань, то зворотній відгук звуком, на натиснення клавіш вельми корисний. І не просто стататично однаковий звук, а щоб на кодну клавішу був різний. Це дещо краще домагає тримати зворотній зв'язок тут і зараз і ще шось там. Після деякого часу, навіть виникає якась незручність, коли клавіші не реагують, якщо набирати щось в тиші прямо. Бо та тиша підступна, залежно від ситуацій та ще чогось. Додатково, фоновий звук, як-то бінауральні ритми, вельми корисні, але потрібно перевіряти що підходить, але це дещо допомагає зняти напруження й краще сконцентруватись.

І найцікавіше, що таку сторіночку на чистому кодові, із звуками клавіш, фоновим звуком, та текстовим віконечком для набору, щоб тектс зберігався в реальному часі, бо раптом оновлення сторінки чи ще шось. Тільки в запиті потрібно писати, щоб була перевірка на припинення конекту звуковхи вузлів, якщо вимкнено або обрано інший варіант, бо може зависати через перевантаження ОЗУ або ще чого. А також згадати, щоб це враховувало натиснення й віртуальних клавіатур смарфона.

І все, така html сторіночка, працює без питань навіть у браізері смарфона. А розмір дуже маленький. Бо основна магія виникає на бочі браузера, тобто його звуковим можливостям генерації звуку на основі математичних моделей. І це кожного разу унікальнрі звуки, що дуже корисно у деяких випадках, щоб замість масового прослуховування одного й того ж звуку, він буд для всіх різний і завжди новий у реальному часі тут і зараз. Ну й там ще багато чогось корисного, тільки ефект для деяких фонових звуків значно кращий у наушниках. Хіба якщо подібне протипоказано, то краще утримуватись від використання.

Нижче наведено приклад такої сторінки, чистий код. Хіба може фоновий шум наче агресивніший, але якщо спочатку поставити на мінімальне, то нічоо наче дощ. достатньо скопіювати, створити новий html файл, або просто текстовий і переменувати його із .txt на .html розширення, вставити туди скопійований текст і зберегти, а потім запустити у браузері ще де:

<!DOCTYPE html>
<html lang="uk">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <title>Bubble Focus - Premium Sound</title>
    <style>
        :root {
            --bg-color: #121212;
            --text-color: #e0e0e0;
            --accent-color: #64b5f6;
            --panel-bg: rgba(30, 30, 30, 0.85);
        }

        body, html {
            margin: 0;
            padding: 0;
            height: 100%;
            font-family: 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
            background-color: var(--bg-color);
            color: var(--text-color);
            overflow: hidden;
            transition: background 0.3s;
        }

        /* Панель керування */
        #controls {
            position: fixed;
            top: 20px;
            left: 50%;
            transform: translateX(-50%);
            width: 90%;
            max-width: 800px;
            display: flex;
            gap: 20px;
            background: var(--panel-bg);
            padding: 15px 25px;
            border-radius: 16px;
            backdrop-filter: blur(12px);
            box-shadow: 0 8px 32px rgba(0,0,0,0.4);
            align-items: center;
            flex-wrap: wrap;
            z-index: 100;
            border: 1px solid rgba(255, 255, 255, 0.1);
        }

        .control-group {
            display: flex;
            flex-direction: column;
            gap: 6px;
            flex: 1;
            min-width: 140px;
        }

        .control-group.full-width {
            flex: 100%;
        }

        label {
            font-size: 11px;
            color: #888;
            text-transform: uppercase;
            letter-spacing: 1px;
            font-weight: 600;
        }

        select, button {
            background: rgba(255, 255, 255, 0.05);
            border: 1px solid rgba(255, 255, 255, 0.15);
            color: white;
            padding: 8px 12px;
            border-radius: 8px;
            font-size: 14px;
            cursor: pointer;
            outline: none;
            transition: all 0.2s;
        }

        select:hover, button:hover {
            background: rgba(255, 255, 255, 0.15);
            border-color: rgba(255, 255, 255, 0.3);
        }

        button {
            font-weight: bold;
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 8px;
        }

        button.active {
            background: var(--accent-color);
            border-color: var(--accent-color);
            color: #000;
        }

        /* Слайдери */
        input[type="range"] {
            -webkit-appearance: none;
            width: 100%;
            height: 6px;
            background: rgba(255,255,255,0.1);
            border-radius: 3px;
            outline: none;
        }

        input[type="range"]::-webkit-slider-thumb {
            -webkit-appearance: none;
            width: 16px;
            height: 16px;
            border-radius: 50%;
            background: var(--accent-color);
            cursor: pointer;
            transition: transform 0.1s;
        }

        input[type="range"]::-webkit-slider-thumb:hover {
            transform: scale(1.2);
        }

        /* Основне поле */
        #editor {
            width: 100%;
            height: 100%;
            background: transparent;
            border: none;
            outline: none;
            color: var(--text-color);
            font-size: 20px;
            line-height: 1.6;
            padding: 120px 20px 40px;
            resize: none;
            display: block;
            box-sizing: border-box;
            overflow-y: auto;
            font-family: 'Georgia', serif; /* Краще для фокусу */
        }

        /* Статус */
        #status {
            position: fixed;
            bottom: 20px;
            right: 20px;
            font-size: 12px;
            color: #666;
            font-style: italic;
        }

        /* Адаптивність для мобільних */
        @media (max-width: 600px) {
            #controls {
                top: 10px;
                left: 10px;
                right: 10px;
                padding: 10px;
                gap: 10px;
            }
            .control-group {
                min-width: 100%;
            }
            #editor {
                padding-top: 140px;
                font-size: 18px;
            }
        }
    </style>
</head>
<body>

    <div id="controls">
        <div class="control-group">
            <button id="toggleSound">🎵 Увімкнути звук</button>
        <div class="control-group">
            <label>Звук клавіш</label>
            <select id="keyType">
                <option value="bubble">Пухирці (Bubble)</option>
                <option value="water">Краплі води (Water)</option>
                <option value="wood">М'яке дерево (Wood)</option>
                <option value="metal">Метал (Metal)</option>
                <option value="soft">М'який клац (Soft)</option>
            </select>
        </div>
        </div>

        <div class="control-group">
            <label>Гучність клавіш</label>
            <input type="range" id="keyVol" min="0" max="1" step="0.01" value="0.4">
        </div>

        <div class="control-group">
            <label>Гучність фону</label>
            <input type="range" id="bgVol" min="0" max="1" step="0.01" value="0.35">
        </div>

        <div class="control-group">
            <label>Тип фону</label>
            <select id="bgType">
                <option value="rain">Дощ (Brown Noise)</option>
                <option value="white">Білий шум</option>
                <option value="none">Тиша</option>
            </select>
        </div>
    </div>

    <div id="status">Збережено</div>
    <textarea id="editor" placeholder="Почніть писати тут..."></textarea>

    <script>
        // --- Конфігурація ---
        const config = {
            keyVolume: 0.4,
            bgVolume: 0.35,
            bgType: 'rain',
            keyType: 'bubble',
            isMuted: false,
            audioCtx: null,
            bgNodes: {
                noise: null,
                filter: null,
                gain: null,
                osc1: null,
                osc2: null
            }
        };

        // Елементи UI
        const editor = document.getElementById('editor');
        const btnToggle = document.getElementById('toggleSound');
        const inputKeyVol = document.getElementById('keyVol');
        const inputBgVol = document.getElementById('bgVol');
        const selectBgType = document.getElementById('bgType');
        const statusEl = document.getElementById('status');

        // 4. Зміна типу звуку клавіш
        const keyTypeSel = document.getElementById('keyType');
        if (keyTypeSel) {
            keyTypeSel.addEventListener('change', (e) => {
                config.keyType = e.target.value;
            });
        }

        // --- Ініціалізація AudioContext ---
        function getAudioContext() {
            if (!config.audioCtx) {
                const AudioContext = window.AudioContext || window.webkitAudioContext;
                config.audioCtx = new AudioContext();
            }
            if (config.audioCtx.state === 'suspended') {
                config.audioCtx.resume();
            }
            return config.audioCtx;
        }

        // --- Генератор звуку клавіатури (Підтримка багатьох типів) ---
        function playBubbleSound() {
            if (config.isMuted || !config.audioCtx) return;

            const ctx = getAudioContext();
            const t = ctx.currentTime;
            const type = config.keyType; // Поточний тип звуку

            // Випадкові параметри для природності
            const randomPitch = 0.8 + Math.random() * 0.4; // 0.8 - 1.2
            const duration = 0.05 + Math.random() * 0.08;

            const osc = ctx.createOscillator();
            const gain = ctx.createGain();
            const filter = ctx.createBiquadFilter();

            // Налаштування за замовчуванням
            osc.frequency.setValueAtTime(800 * randomPitch, t);
            filter.type = 'lowpass';
            filter.frequency.value = 2000;
            filter.Q.value = 0.1;

            switch (type) {
                case 'bubble':
                    // Пухирці: синусоїда, швидке падіння частоти
                    osc.type = 'sine';
                    osc.frequency.exponentialRampToValueAtTime(200 * randomPitch, t + duration);
                    filter.frequency.exponentialRampToValueAtTime(100, t + duration);
                    gain.gain.setValueAtTime(config.keyVolume, t);
                    gain.gain.exponentialRampToValueAtTime(0.001, t + duration);
                    osc.start(t);
                    osc.stop(t + duration);
                    break;

                case 'water':
                    // Краплі: трикутна хвиля, м'яке затухання
                    osc.type = 'triangle';
                    osc.frequency.exponentialRampToValueAtTime(100 * randomPitch, t + duration);
                    filter.frequency.exponentialRampToValueAtTime(500, t + duration);
                    gain.gain.setValueAtTime(config.keyVolume * 0.8, t);
                    gain.gain.exponentialRampToValueAtTime(0.001, t + duration);
                    osc.start(t);
                    osc.stop(t + duration);
                    break;

                case 'wood':
                    // Дерево: низька частота, теплий звук
                    osc.type = 'sine';
                    osc.frequency.setValueAtTime(300 * randomPitch, t);
                    osc.frequency.exponentialRampToValueAtTime(150 * randomPitch, t + 0.1);
                    filter.type = 'lowpass';
                    filter.frequency.value = 600;
                    filter.Q.value = 1.5;
                    gain.gain.setValueAtTime(config.keyVolume * 0.9, t);
                    gain.gain.exponentialRampToValueAtTime(0.001, t + 0.15);
                    osc.start(t);
                    osc.stop(t + 0.15);
                    break;

                case 'metal':
                    // Метал: висока частота, дитячий дзвін
                    osc.type = 'sine';
                    osc.frequency.setValueAtTime(1200 * randomPitch, t);
                    osc.frequency.exponentialRampToValueAtTime(800 * randomPitch, t + 0.05);
                    filter.type = 'highpass';
                    filter.frequency.value = 800;
                    gain.gain.setValueAtTime(config.keyVolume * 0.6, t);
                    gain.gain.exponentialRampToValueAtTime(0.001, t + 0.08);
                    osc.start(t);
                    osc.stop(t + 0.08);
                    break;

                case 'soft':
                    // М'який клац: дуже короткий, без відлуння
                    osc.type = 'square';
                    osc.frequency.setValueAtTime(600 * randomPitch, t);
                    gain.gain.setValueAtTime(config.keyVolume * 0.4, t);
                    gain.gain.exponentialRampToValueAtTime(0.001, t + 0.02);
                    osc.start(t);
                    osc.stop(t + 0.02);
                    break;
            }

            // З'єднання вузлів
            osc.connect(filter);
            filter.connect(gain);
            gain.connect(ctx.destination);

            // Очищення пам'яті
            setTimeout(() => {
                osc.disconnect();
                filter.disconnect();
                gain.disconnect();
            }, duration * 1000 + 50);
        }

        // --- Генерація фону (Дощ + Бінауральний ритм) ---
        function startBackgroundSound() {
            const ctx = getAudioContext();

            // Очищення попередніх вузлів
            stopBackgroundSound();

            if (config.bgType === 'none') return;

            // 1. Створення шуму (Brown Noise для дощу)
            const bufferSize = ctx.sampleRate * 2; // 2 секунди буфер
            const buffer = ctx.createBuffer(1, bufferSize, ctx.sampleRate);
            const data = buffer.getChannelData(0);

            if (config.bgType === 'rain') {
                // Brownian noise (глибокий шум)
                let lastOut = 0;
                for (let i = 0; i < bufferSize; i++) {
                    const white = Math.random() * 2 - 1;
                    data[i] = (lastOut + (0.02 * white)) / 1.02;
                    lastOut = data[i];
                    data[i] *= 3.5; // Компенсація гучності
                }
            } else {
                // White noise
                for (let i = 0; i < bufferSize; i++) {
                    data[i] = Math.random() * 2 - 1;
                }
            }

            const noiseNode = ctx.createBufferSource();
            noiseNode.buffer = buffer;
            noiseNode.loop = true;

            // Фільтр для дощу (Lowpass 800Hz)
            const filter = ctx.createBiquadFilter();
            filter.type = 'lowpass';
            filter.frequency.value = config.bgType === 'rain' ? 800 : 2000;
            filter.Q.value = 0.5;

            // 2. Бінауральний ритм (Oscillators)
            const osc1 = ctx.createOscillator();
            const osc2 = ctx.createOscillator();
            const oscGain = ctx.createGain();

            osc1.type = 'sine';
            osc2.type = 'sine';

            // Частоти: 200 Гц і 240 Гц (різниця 40 Гц - Alpha/Theta ритм)
            osc1.frequency.value = 200;
            osc2.frequency.value = 240;

            oscGain.gain.value = config.bgVolume * 0.15; // Дуже тихо під фоном

            // 3. Головний гейн для фону
            const bgGain = ctx.createGain();
            bgGain.gain.value = config.bgVolume;

            // З'єднання
            noiseNode.connect(filter);
            filter.connect(bgGain);

            osc1.connect(oscGain);
            osc2.connect(oscGain);
            oscGain.connect(bgGain);

            bgGain.connect(ctx.destination);

            // Запуск
            noiseNode.start();
            osc1.start();
            osc2.start();

            // Збереження посилань для зупинки
            config.bgNodes = {
                noise: noiseNode,
                filter: filter,
                gain: bgGain,
                osc1: osc1,
                osc2: osc2,
                oscGain: oscGain
            };
        }

        function stopBackgroundSound() {
            if (config.bgNodes.noise) {
                config.bgNodes.noise.stop();
                config.bgNodes.noise.disconnect();
            }
            if (config.bgNodes.osc1) {
                config.bgNodes.osc1.stop();
                config.bgNodes.osc1.disconnect();
            }
            if (config.bgNodes.osc2) {
                config.bgNodes.osc2.stop();
                config.bgNodes.osc2.disconnect();
            }
            // Очищення
            config.bgNodes = { noise: null, filter: null, gain: null, osc1: null, osc2: null, oscGain: null };
        }

        // --- Події ---

        // 1. Кнопка вимкнення/ввімкнення
        btnToggle.addEventListener('click', () => {
            config.isMuted = !config.isMuted;

            if (config.isMuted) {
                btnToggle.textContent = "🔇 Увімкнути звук";
                btnToggle.classList.remove('active');
                stopBackgroundSound();
            } else {
                btnToggle.textContent = "🔊 Вимкнути звук";
                btnToggle.classList.add('active');
                startBackgroundSound();
                // Ініціалізуємо контекст
                getAudioContext();
            }
        });


        // 2. Регулювання гучності клавіш
        inputKeyVol.addEventListener('input', (e) => {
            config.keyVolume = parseFloat(e.target.value);
        });

        // 3. Регулювання гучності фону
        inputBgVol.addEventListener('input', (e) => {
            const newVol = parseFloat(e.target.value);
            config.bgVolume = newVol;

            // Миттєве оновлення гучності фону, якщо він грає
            if (config.bgNodes.gain) {
                config.bgNodes.gain.gain.setTargetAtTime(newVol, getAudioContext().currentTime, 0.1);
            }
            // Оновлення гучності бінауральних тонів
            if (config.bgNodes.oscGain) {
                config.bgNodes.oscGain.gain.setTargetAtTime(newVol * 0.15, getAudioContext().currentTime, 0.1);
            }
        });

        // 4. Зміна типу фону
        selectBgType.addEventListener('change', (e) => {
            config.bgType = e.target.value;
            if (!config.isMuted && config.audioCtx) {
                // Плавне перемикання (зупинка старих, запуск нових)
                stopBackgroundSound();
                startBackgroundSound();
            }
        });

        // 5. Обробка введення тексту (звук + збереження)
        editor.addEventListener('input', () => {
            // Грати звук
            playBubbleSound();

            // Автозбереження в LocalStorage
            localStorage.setItem('bubble_focus_text', editor.value);

            // Візуальний індикатор
            statusEl.textContent = "Збереження...";
            statusEl.style.opacity = '1';

            // Повернути статус "Збережено" через 1 секунду
            clearTimeout(window.saveTimer);
            window.saveTimer = setTimeout(() => {
                statusEl.textContent = "Збережено";
                statusEl.style.opacity = '0.7';
            }, 1000);
        });

        // 6. Завантаження збереженого тексту при старті
        window.addEventListener('load', () => {
            const saved = localStorage.getItem('bubble_focus_text');
            if (saved) {
                editor.value = saved;
            }

            // Ініціалізація AudioContext (браузери вимагають дії користувача)
            // Ми не запускаємо звук одразу, чекаємо на перше натискання або клік по кнопці
            // Але створюємо контекст, щоб не було проблем з блокуванням
            if (!config.audioCtx) {
                const AudioContext = window.AudioContext || window.webkitAudioContext;
                config.audioCtx = new AudioContext();
            }
        });

        // 7. Запобігання втрати даних при закритті вкладки
        window.addEventListener('beforeunload', () => {
            localStorage.setItem('bubble_focus_text', editor.value);
        });

    </script>
</body>
</html>
Sort:  
 8 days ago (edited)

Такі фішки, звичайно, на того, хто таке легко сприймає. Кому зайві шумові ефекти не заважають. То ясно... А ще для тих, хто на кодах розуміється))

Hello @luxalok

I hope you are doing well.

I would like to kindly ask for your support by voting for @friends.witness.

Our team is continuously working to improve the Steem ecosystem through community-driven development. We are building SteemOne, an all-in-one platform with useful tools and services for Steem users, and we continue to invest our own time and resources into its development.

We are also developing a modern multiplayer Snake game for the Steem ecosystem. It aims to be the first advanced real-time multiplayer Snake game on Steem, allowing users to play together in an interactive and engaging environment. We believe this project will bring more activity, innovation, and new users to the Steem blockchain.

Your witness vote would mean a lot to us and help us continue building these projects for the community.

You can vote for @friends.witness here:
https://steemitwallet.com/~witnesses

Thank you very much for your time and consideration. We truly appreciate your support.