├── .bumpversion.cfg ├── .gitattributes ├── .github ├── FUNDING.yml └── workflows │ └── publish.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── __init__.py ├── assets ├── images │ ├── example_001_web_viewer.gif │ ├── example_002_instant_queue_key_control.gif │ ├── example_003_preview_image_in_background.png │ ├── sample_pic_01_woman_head.png │ ├── sample_pic_02_empty_room.png │ ├── sample_pic_03_hallway.jpg │ ├── sample_pic_04_abstract_drawing.png │ ├── sample_pic_05_harley_quinn.png │ └── troubleshooting_001.png ├── models │ └── music_genre_cnn.pth ├── touchosc │ └── comfyui_osc_control.tosc └── videos │ ├── intro.mp4 │ └── intro_thumbnail.jpg ├── docs ├── audio_nodes.md ├── gamepad_nodes.md ├── image_nodes.md ├── key_control_nodes.md ├── logic_nodes.md ├── midi_nodes.md ├── osc_control_nodes.md ├── text_nodes.md ├── tutorial_001_live_portrait_with_gamepad.md ├── tutorial_002_fast_image_to_video_by_ltx_video_and_ollama.md ├── tutorial_003_fast_image_to_video_by_ltx_video_and_mmaudio_and_ollama.md ├── tutorial_004_real_time_voice_clone_by_f5_tts.md ├── tutorial_005_storytelling_with_text_srt_player.md ├── tutorial_006_srt_to_audio_picture_book.md ├── tutorial_007_live_portrait_with_native_gamepad.md ├── web_viewer_nodes.md ├── web_viewer_nodes_extra_params.md ├── websocket_nodes.md └── websocket_nodes_extra_params.md ├── example_workflows ├── example_audio_nodes_001_audio_recorder.jpg ├── example_audio_nodes_001_audio_recorder.json ├── example_audio_nodes_001_audio_recorder.png ├── example_gamepad_nodes_001_basic.jpeg ├── example_gamepad_nodes_001_basic.jpg ├── example_gamepad_nodes_001_basic.json ├── example_gamepad_nodes_001_basic.png ├── example_gamepad_nodes_002_live_portrait.jpeg ├── example_gamepad_nodes_002_live_portrait.json ├── example_gamepad_nodes_002_live_portrait.png ├── example_image_nodes_001_preview_in_background.jpg ├── example_image_nodes_001_preview_in_background.json ├── example_image_nodes_001_preview_in_background.png ├── example_key_control_001_basic.jpg ├── example_key_control_001_basic.json ├── example_key_control_001_basic.png ├── example_osc_control_001_basic.jpg ├── example_osc_control_001_basic.json ├── example_osc_control_001_basic.png ├── example_osc_control_002_live_portrait_with_gamepad.jpg ├── example_osc_control_002_live_portrait_with_gamepad.json ├── example_osc_control_002_live_portrait_with_gamepad.png ├── example_osc_control_003_ic-light.jpg ├── example_osc_control_003_ic-light.json ├── example_osc_control_003_ic-light.png ├── example_osc_control_004_ic-light_and_live_portrait.jpg ├── example_osc_control_004_ic-light_and_live_portrait.json ├── example_osc_control_004_ic-light_and_live_portrait.png ├── example_others_001_text_to_image_8k.jpg ├── example_others_001_text_to_image_8k.json ├── example_others_001_text_to_image_8k.png ├── example_others_002_autorun_instant_queue.jpg ├── example_others_002_autorun_instant_queue.json ├── example_others_002_autorun_instant_queue.png ├── example_others_003_text_to_image_5k_avp_ultra_wide.jpg ├── example_others_003_text_to_image_5k_avp_ultra_wide.json ├── example_others_003_text_to_image_5k_avp_ultra_wide.png ├── example_others_004_srt_to_audio_picture_book.jpg ├── example_others_004_srt_to_audio_picture_book.json ├── example_others_004_srt_to_audio_picture_book.png ├── example_others_005_srt_to_audio_picture_book_sdxl.jpg ├── example_others_005_srt_to_audio_picture_book_sdxl.json ├── example_others_005_srt_to_audio_picture_book_sdxl.png ├── example_text_nodes_001_text_srt_player.jpg ├── example_text_nodes_001_text_srt_player.json ├── example_text_nodes_001_text_srt_player.png ├── example_web_viewer_001_image_web_viewer.jpg ├── example_web_viewer_001_image_web_viewer.json ├── example_web_viewer_001_image_web_viewer.png ├── example_web_viewer_002_image_flipbook_web_viewer.jpg ├── example_web_viewer_002_image_flipbook_web_viewer.json ├── example_web_viewer_002_image_flipbook_web_viewer.png ├── example_web_viewer_003_video_web_viewer.jpg ├── example_web_viewer_003_video_web_viewer.json ├── example_web_viewer_003_video_web_viewer.png ├── example_web_viewer_004_video_web_viewer_video_with_sfx.jpg ├── example_web_viewer_004_video_web_viewer_video_with_sfx.json ├── example_web_viewer_004_video_web_viewer_video_with_sfx.png ├── example_web_viewer_005_audio_web_viewer_f5_tts.jpg ├── example_web_viewer_005_audio_web_viewer_f5_tts.json ├── example_web_viewer_005_audio_web_viewer_f5_tts.png ├── example_websocket_nodes_001_basic.jpeg ├── example_websocket_nodes_001_basic.json └── example_websocket_nodes_001_basic.png ├── https ├── cert.pem └── key.pem ├── nodes ├── audio_nodes.py ├── gamepad_nodes.py ├── image_nodes.py ├── key_control_nodes.py ├── logic_nodes.py ├── midi_nodes.py ├── node_utils.py ├── osc_control_nodes.py ├── test │ └── osc_server_test.py ├── text_nodes.py ├── web_viewer_nodes.py └── websocket_nodes.py ├── pyproject.toml ├── requirements.txt ├── update_version.py ├── utils ├── genres.json └── music_genres_classifier.py └── web ├── comfyui ├── audio_mic_loader_node.js ├── audio_nodes.js ├── gamepad_nodes.js ├── image_nodes.js ├── key_control_nodes.js ├── logic_nodes.js ├── midi_nodes.js ├── node_utils.js ├── text_nodes.js ├── web_viewer_nodes.js └── websocket_nodes.js ├── css └── style.css ├── index.html └── scripts └── script.js /.bumpversion.cfg: -------------------------------------------------------------------------------- 1 | [bumpversion] 2 | current_version = 1.0.33 3 | commit = True 4 | tag = True 5 | parse = (?P\d+)\.(?P\d+)\.(?P\d+) 6 | serialize = {major}.{minor}.{patch} 7 | 8 | [bumpversion:file:__init__.py] 9 | 10 | [bumpversion:file:CHANGELOG.md] 11 | search = Unreleased 12 | replace = {new_version} - {now:%Y-%m-%d} 13 | 14 | [bumpversion:file:pyproject.toml] 15 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # disalbe Git LFS for .pth files as github quota is exceeded 2 | # *.pth filter=lfs diff=lfs merge=lfs -text 3 | ./assets/models/music_genre_cnn.pth !text !filter !merge !diff 4 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: VrchStudio -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish to Comfy registry 2 | on: 3 | workflow_dispatch: 4 | push: 5 | branches: 6 | - main 7 | - master 8 | paths: 9 | - "pyproject.toml" 10 | 11 | permissions: 12 | issues: write 13 | 14 | jobs: 15 | publish-node: 16 | name: Publish Custom Node to registry 17 | runs-on: ubuntu-latest 18 | # if this is a forked repository. Skipping the workflow. 19 | if: ${{ github.repository_owner == 'VrchStudio' }} 20 | steps: 21 | - name: Check out code 22 | uses: actions/checkout@v4 23 | - name: Publish Custom Node 24 | uses: Comfy-Org/publish-node-action@v1 25 | with: 26 | ## Add your own personal access token to your Github Repository secrets and reference it here. 27 | personal_access_token: ${{ secrets.REGISTRY_ACCESS_TOKEN }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Python 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | *.so 6 | 7 | # Virtual Environment 8 | venv/ 9 | env/ 10 | ENV/ 11 | 12 | # IDEs and Editors 13 | .idea/ 14 | .vscode/ 15 | *.swp 16 | *.swo 17 | *~ 18 | 19 | # OS generated files 20 | .DS_Store 21 | .DS_Store? 22 | ._* 23 | .Spotlight-V100 24 | .Trashes 25 | ehthumbs.db 26 | Thumbs.db 27 | 28 | # ComfyUI specific 29 | web/temp_image.png 30 | 31 | # Logs 32 | *.log 33 | 34 | # Temporary files 35 | *.tmp 36 | 37 | # Distribution / packaging 38 | .Python 39 | build/ 40 | develop-eggs/ 41 | dist/ 42 | downloads/ 43 | eggs/ 44 | .eggs/ 45 | lib/ 46 | lib64/ 47 | parts/ 48 | sdist/ 49 | var/ 50 | wheels/ 51 | *.egg-info/ 52 | .installed.cfg 53 | *.egg 54 | 55 | # Jupyter Notebook 56 | .ipynb_checkpoints 57 | 58 | # pyenv 59 | .python-version 60 | 61 | # pipenv 62 | Pipfile.lock 63 | 64 | # Environment variables 65 | .env 66 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 vrch.io 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ComfyUI Web Viewer 2 | 3 | The **ComfyUI Web Viewer** by [vrch.ai](https://vrch.ai) is a custom node collection offering a real-time AI-generated interactive art framework. This utility integrates realtime streaming into ComfyUI workflows, supporting keyboard control nodes, OSC control nodes, sound input nodes, and more. Accessible from any device with a web browser, it enables real time interaction with AI-generated content, making it ideal for interactive visual projects and enhancing ComfyUI workflows with efficient content management and display. 4 | 5 | [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/VrchStudio/comfyui-web-viewer) 6 | 7 | **✨ Features:** 8 | 9 | - **Real-Time AI Generation & Interaction**: Immediate response for interactive creativity. 10 | - **Multi-Input Support**: Easily integrates keyboard, OSC, and audio input for versatile interactivity. 11 | - **Universal Web Accessibility**: Compatible with any device equipped with a web browser. 12 | 13 | **🚀 Support Us:** 14 | 15 | If you find the **ComfyUI Web Viewer** useful or inspiring, consider supporting us: 16 | 17 | - 💖 **Sponsor**: Help us maintain and enhance the project through [GitHub Sponsors](https://github.com/sponsors/VrchStudio). 18 | - ⭐ **Star the Project**: A star on GitHub greatly motivates us and helps increase visibility! 19 | - 📩 **Business Inquiries**: For commercial collaborations, reach us at [hi@vrch.io](mailto:hi@vrch.io?subject=ComfyUI%20Web%20Viewer%20-%20Commercial%20Inquiry). 20 | 21 | 23 | 24 | ## Changelog 25 | 26 | see [CHANGELOG](CHANGELOG.md) 27 | 28 | ## Installation 29 | 30 | ### Method 1: Auto Installation (Recommended) 31 | 32 | Simply search for `ComfyUI Web Viewer` in ComfyUI Manager and install it directly. 33 | 34 | ### Method 2: Manual Installation 35 | 36 | 1. Clone this repo into the `custom_nodes` directory of ComfyUI 37 | ``` 38 | git clone git@github.com:VrchStudio/comfyui-web-viewer.git 39 | ``` 40 | 2. Install dependencies: 41 | ``` 42 | pip install -r requirements.txt 43 | ``` 44 | or if you use the `windows` portable install, run this in `ComfyUI_windows_portable` folder: 45 | ``` 46 | python_embeded\python.exe -m pip install -r ComfyUI\custom_nodes\comfyui-web-viewer\requirements.txt 47 | ``` 48 | 3. Restart ComfyUI 49 | 50 | ## How to Use 51 | 52 | ### `Web Viewer Nodes` 53 | 54 | ![](./assets/images/example_001_web_viewer.gif) 55 | 56 | - Documentation: [Usage of Web Viewer nodes](./docs/web_viewer_nodes.md) 57 | - Example workflows: 58 | - [Workflow Example: Image Web Viewer node](./example_workflows/example_web_viewer_001_image_web_viewer.json) 59 | - [Workflow Example: Image Flipbook Viewer node](./example_workflows/example_web_viewer_002_image_flipbook_web_viewer.json) 60 | - [Workflow Example: Video Web Viewer node (video only)](./example_workflows/example_web_viewer_003_video_web_viewer.json) 61 | - [Workflow Example: Video Web Viewer node (video + sound)](./example_workflows/example_web_viewer_004_video_web_viewer_video_with_sfx.json) 62 | - [Workflow Example: Audio Web Viewer node (voice clone)](./example_workflows/example_web_viewer_005_audio_web_viewer_f5_tts.json) 63 | - Tutorials: 64 | - [Fast Image to Video by LTX Videos and Ollama](./docs/tutorial_002_fast_image_to_video_by_ltx_video_and_ollama.md) 65 | - [Fast Image to Video by LTX Vidoes, MMAudio and Ollama](./docs/tutorial_003_fast_image_to_video_by_ltx_video_and_mmaudio_and_ollama.md) 66 | - [Real Time Voice Clone](./docs/tutorial_004_real_time_voice_clone_by_f5_tts.md) 67 | - [Audio Picture Book with Your Own Voice](./docs/tutorial_006_srt_to_audio_picture_book.md) 68 | 69 | ### `WebSocket Web Viewer Nodes` 70 | 71 | - Documentation: [Usage of WebSocket Viewer nodes](./docs/websocket_nodes.md) 72 | - Example workflows: 73 | - [Workflow Example: WebSocket Nodes](./example_workflows/example_websocket_nodes_001_basic.json) 74 | - Tutorials: n/a 75 | 76 | ### `OSC Control Nodes` 77 | 78 | - Documentation: [Usage of OSC Control nodes](./docs/osc_control_nodes.md) 79 | - TouchOSC Control Panel: 80 | - [comfyui_osc_control.tosc](./assets/touchosc/comfyui_osc_control.tosc) 81 | - Example workflows: 82 | - [Workflow Example: OSC Control Nodes](./example_workflows/example_osc_control_001_basic.json) 83 | - [Workflow Example: Live Portrait + Gamepad](./example_workflows/example_osc_control_002_live_portrait_with_gamepad.json) 84 | - [Workflow Example: IC-Light](./example_workflows/example_osc_control_003_ic-light.png) 85 | - Tutorials: 86 | - [Live Portrait + Gamepad](./docs/tutorial_001_live_portrait_with_gamepad.md) 87 | 88 | ### `Key Control Nodes` 89 | 90 | - Documentation: [Usage of Key Control nodes](./docs/key_control_nodes.md) 91 | - Example workflows: 92 | - [Workflow Example: Key Control Nodes](./example_workflows/example_key_control_001_basic.json) 93 | 94 | ### `Gamepad Nodes` 95 | 96 | - Documentation: [Usage of Gamepad nodes](./docs/gamepad_nodes.md) 97 | - Example workflows: 98 | - [Workflow Example: Gamepad Nodes](./example_workflows/example_gamepad_nodes_001_basic.json) 99 | - [Workflow Example: Gamepad with Live Portrait](./example_workflows/example_gamepad_nodes_002_live_portrait.json) 100 | 101 | ### `Midi Device Nodes` 102 | 103 | - Documentation: [Usage of Midi Device nodes](./docs/midi_nodes.md) 104 | - Example workflows: TBA 105 | 106 | ### `Audio Nodes` 107 | 108 | - Documentation: [Usage of Audio nodes](./docs/audio_nodes.md) 109 | - Example workflows: 110 | - [Workflow Example: Audio Recorder Node](./example_workflows/example_audio_nodes_001_audio_recorder.json) 111 | 112 | ### `Image Nodes` 113 | 114 | - Documentation: [Usage of Image nodes](./docs/image_nodes.md) 115 | - Example workflows: 116 | - [Workflow Example: Preview Image in Background](./example_workflows/example_image_nodes_001_preview_in_background.json) 117 | 118 | ### `Logic Nodes` 119 | 120 | - Documentation: [Usage of Logic nodes](./docs/logic_nodes.md) 121 | - Example workflows: n/a 122 | 123 | ### `Text Nodes` 124 | 125 | - Documentation: [Usage of Text nodes](./docs/text_nodes.md) 126 | - Example workflows: 127 | - [Workflow Example: Text SRT Player Node](./example_workflows/example_text_nodes_001_text_srt_player.json) 128 | - Tutorials: 129 | - [Storytelling with Text SRT Player](./docs/tutorial_005_storytelling_with_text_srt_player.md) 130 | 131 | ### `Other Example Workflows` 132 | 133 | - [Rapid Text to Image (8K)](./example_workflows/example_others_001_text_to_image_8k.json) 134 | - [Auto Switch to Instant Queue and Run Workflow](./example_workflows/example_others_002_autorun_instant_queue.json) 135 | - [Apple Vision Pro Ultrawide Wallpaper (5K)](./example_workflows/example_others_003_text_to_image_5k_avp_ultra_wide.json) 136 | - [Audio Picture Book with Your Own Voice (FLUX version)](./example_workflows/example_others_004_srt_to_audio_picture_book.json) 137 | - [Audio Picture Book with Your Own Voice (SDXL version)](./example_workflows/example_others_005_srt_to_audio_picture_book_sdxl.json) 138 | 139 | ## Troubleshootings 140 | 141 | ### 1. Image Not Displayed in Popped-Up Image Viewer Window 142 | 143 | For Chrome, you may need to add the ComfyUI server's IP address manually in `chrome://flags/#unsafely-treat-insecure-origin-as-secure` to enable access to the content. For other browsers, an http-to-http setup should allow for seamless access without additional configuration. 144 | 145 | ![](./assets/images/troubleshooting_001.png) 146 | 147 | --- 148 | 149 | ### 2. How can I resolve the CORS policy error when trying to display images? 150 | 151 | If you’re encountering a CORS policy error with a message like this: 152 | 153 | > `"origin 'https://vrch.ai' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource"` 154 | 155 | or 156 | 157 | > `"WARNING: request with non matching host and origin 127.0.0.1 !=vrch.ai, returning 403"` 158 | 159 | you can resolve this by launching the ComfyUI service with the `--enable-cors-header` flag appended. For example: 160 | 161 | ```bash 162 | python main.py --enable-cors-header 163 | ``` 164 | 165 | For additional details, refer to [this discussion on GitHub](https://github.com/comfyanonymous/ComfyUI/pull/413#issuecomment-1499518110). 166 | 167 | --- 168 | 169 | ### 3. Why can’t the ComfyUI service run at HTTPS/TLS/SSL on port 8189? 170 | 171 | `ComfyUI Web Viewer` provides a build-in, self-signed certificate (intended for development only, not production use). To launch the ComfyUI service with HTTPS enabled on port 8189, use the following command: 172 | 173 | ```bash 174 | # Start ComfyUI with HTTPS using the built-in certificate and key 175 | python main.py --tls-keyfile ./custom_nodes/comfyui-web-viewer/https/key.pem --tls-certfile ./custom_nodes/comfyui-web-viewer/https/cert.pem --port 8189 --listen 176 | ``` 177 | 178 | For more details, refer to the [ComfyUI official instructions](https://github.com/comfyanonymous/ComfyUI?tab=readme-ov-file#how-to-use-tlsssl). 179 | 180 | 181 | ## Version Update 182 | 183 | This project uses `bump2version` for version management. To update the version: 184 | 185 | 1. Ensure you have `bump2version` installed: 186 | ```bash 187 | pip install bump2version 188 | ``` 189 | 2. To update the version, run: 190 | ```bash 191 | python update_version.py [major|minor|patch] 192 | ``` 193 | Replace `[major|minor|patch]` with the part of the version you want to increment. 194 | 3. This will automatically: 195 | - Update the version number in `__init__.py` 196 | - Update the CHANGELOG.md file 197 | - Create a new git commit and tag (if you're using git) 198 | 4. After running the script, review and update the CHANGELOG.md file with details about the new version's changes. 199 | - Note: make sure you've put changes in `Unreleased` section manually 200 | 201 | ## Contributing 202 | 203 | Created and maintained by the [vrch.io](https://vrch.io) team. 204 | 205 | Contributions are welcome! Please feel free to submit a Pull Request. 206 | 207 | ## Contact Us 208 | 209 | For any inquiries, you can contact us at [hi@vrch.io](mailto:hi@vrch.io?subject=ComfyUI%20Web%20Viewer%20-%20General%20Inquiry). 210 | 211 | ## Star History 212 | 213 | [![Star History Chart](https://api.star-history.com/svg?repos=VrchStudio/comfyui-web-viewer&type=Date)](https://www.star-history.com/#VrchStudio/comfyui-web-viewer&Date) 214 | 215 | ## License 216 | 217 | [MIT License](LICENSE) 218 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | from .nodes.web_viewer_nodes import * 2 | from .nodes.image_nodes import * 3 | from .nodes.audio_nodes import * 4 | from .nodes.text_nodes import * 5 | from .nodes.key_control_nodes import * 6 | from .nodes.osc_control_nodes import * 7 | from .nodes.websocket_nodes import * 8 | from .nodes.gamepad_nodes import * 9 | from .nodes.logic_nodes import * 10 | from .nodes.midi_nodes import * 11 | 12 | __version__ = "1.0.33" 13 | 14 | NODE_CLASS_MAPPINGS = { 15 | "VrchAnyOSCControlNode": VrchAnyOSCControlNode, 16 | "VrchAudioChannelLoaderNode": VrchAudioChannelLoaderNode, 17 | "VrchAudioConcatNode": VrchAudioConcatNode, 18 | "VrchAudioGenresNode": VrchAudioGenresNode, 19 | "VrchAudioRecorderNode": VrchAudioRecorderNode, 20 | "VrchAudioSaverNode": VrchAudioSaverNode, 21 | "VrchAudioWebViewerNode": VrchAudioWebViewerNode, 22 | "VrchMicLoaderNode": VrchMicLoaderNode, 23 | "VrchBooleanKeyControlNode": VrchBooleanKeyControlNode, 24 | "VrchChannelOSCControlNode": VrchChannelOSCControlNode, 25 | "VrchChannelX4OSCControlNode": VrchChannelX4OSCControlNode, 26 | "VrchDelayOSCControlNode": VrchDelayOscControlNode, 27 | "VrchFloatKeyControlNode": VrchFloatKeyControlNode, 28 | "VrchFloatOSCControlNode": VrchFloatOSCControlNode, 29 | "VrchFloatRemapNode": VrchFloatRemapNode, 30 | "VrchGamepadLoaderNode": VrchGamepadLoaderNode, 31 | "VrchImageChannelLoaderNode": VrchImageChannelLoaderNode, 32 | "VrchImageFlipBookWebViewerNode": VrchImageFlipBookWebViewerNode, 33 | "VrchImagePreviewBackgroundNode": VrchImagePreviewBackgroundNode, 34 | "VrchImagePreviewBackgroundNewNode": VrchImagePreviewBackgroundNewNode, 35 | "VrchImageSaverNode": VrchImageSaverNode, 36 | "VrchImageSwitchOSCControlNode": VrchImageSwitchOSCControlNode, 37 | "VrchImageWebSocketChannelLoaderNode": VrchImageWebSocketChannelLoaderNode, 38 | "VrchImageWebSocketWebViewerNode": VrchImageWebSocketWebViewerNode, 39 | "VrchImageWebViewerNode": VrchImageWebViewerNode, 40 | "VrchInstantQueueKeyControlNode": VrchInstantQueueKeyControlNode, 41 | "VrchIntKeyControlNode": VrchIntKeyControlNode, 42 | "VrchIntOSCControlNode": VrchIntOSCControlNode, 43 | "VrchIntRemapNode": VrchIntRemapNode, 44 | "VrchJsonUrlLoaderNode": VrchJsonUrlLoaderNode, 45 | "VrchJsonWebSocketChannelLoaderNode": VrchJsonWebSocketChannelLoaderNode, 46 | "VrchJsonWebSocketSenderNode": VrchJsonWebSocketSenderNode, 47 | "VrchMidiDeviceLoaderNode": VrchMidiDeviceLoaderNode, 48 | "VrchModelWebViewerNode": VrchModelWebViewerNode, 49 | "VrchOSCControlSettingsNode": VrchOSCControlSettingsNode, 50 | "VrchSwitchOSCControlNode": VrchSwitchOSCControlNode, 51 | "VrchTextConcatOSCControlNode": VrchTextConcatOSCControlNode, 52 | "VrchTextKeyControlNode": VrchTextKeyControlNode, 53 | "VrchTextSrtPlayerNode": VrchTextSrtPlayerNode, 54 | "VrchTextSwitchOSCControlNode": VrchTextSwitchOSCControlNode, 55 | "VrchTriggerToggleNode": VrchTriggerToggleNode, 56 | "VrchTriggerToggleX4Node": VrchTriggerToggleX4Node, 57 | "VrchTriggerToggleX8Node": VrchTriggerToggleX8Node, 58 | "VrchVideoWebViewerNode": VrchVideoWebViewerNode, 59 | "VrchWebSocketServerNode": VrchWebSocketServerNode, 60 | "VrchWebViewerNode": VrchWebViewerNode, 61 | "VrchXboxControllerNode": VrchXboxControllerNode, 62 | "VrchXYOSCControlNode": VrchXYOSCControlNode, 63 | "VrchXYZOSCControlNode": VrchXYZOSCControlNode, 64 | } 65 | 66 | NODE_DISPLAY_NAME_MAPPINGS = { 67 | "VrchAnyOSCControlNode": "ANY Value OSC Control @ vrch.ai", 68 | "VrchAudioChannelLoaderNode": "AUDIO Web Viewer Channel Loader @ vrch.ai", 69 | "VrchAudioConcatNode": "AUDIO Concat @ vrch.ai", 70 | "VrchAudioGenresNode": "AUDIO Get Genres @ vrch.ai", 71 | "VrchAudioRecorderNode": "AUDIO Recorder @ vrch.ai", 72 | "VrchAudioSaverNode": "AUDIO Saver @ vrch.ai", 73 | "VrchAudioWebViewerNode": "AUDIO Web Viewer @ vrch.ai", 74 | "VrchMicLoaderNode": "Microphone Loader @ vrch.ai", 75 | "VrchBooleanKeyControlNode": "BOOLEAN Key Control @ vrch.ai", 76 | "VrchChannelOSCControlNode": "CHANNEL OSC Control @ vrch.ai", 77 | "VrchChannelX4OSCControlNode": "CHANNEL x4 OSC Control @ vrch.ai", 78 | "VrchDelayOscControlNode": "Delay OSC Control @ vrch.ai", 79 | "VrchFloatKeyControlNode": "FLOAT Key Control @ vrch.ai", 80 | "VrchFloatOSCControlNode": "FLOAT OSC Control @ vrch.ai", 81 | "VrchFloatRemapNode": "FLOAT Remap @ vrch.ai", 82 | "VrchGamepadLoaderNode": "Gamepad Loader @ vrch.ai", 83 | "VrchImageChannelLoaderNode": "IMAGE Web Viewer Channel Loader @ vrch.ai", 84 | "VrchImageFlipBookWebViewerNode": "IMAGE Flipbook Web Viewer @ vrch.ai", 85 | "VrchImagePreviewBackgroundNode": "IMAGE Preview in Background (Legacy) @ vrch.ai", 86 | "VrchImagePreviewBackgroundNewNode": "IMAGE Preview in Background @ vrch.ai", 87 | "VrchImageSaverNode": "IMAGE Saver @ vrch.ai", 88 | "VrchImageSwitchOSCControlNode": "IMAGE Switch OSC Control @ vrch.ai", 89 | "VrchImageWebSocketChannelLoaderNode": "IMAGE WebSocket Channel Loader @ vrch.ai", 90 | "VrchImageWebSocketWebViewerNode": "IMAGE WebSocket Web Viewer @ vrch.ai", 91 | "VrchImageWebViewerNode": "IMAGE Web Viewer @ vrch.ai", 92 | "VrchInstantQueueKeyControlNode": "Instant Queue Key Control @ vrch.ai", 93 | "VrchIntKeyControlNode": "INT Key Control @ vrch.ai", 94 | "VrchIntOSCControlNode": "INT OSC Control @ vrch.ai", 95 | "VrchIntRemapNode": "INT Remap @ vrch.ai", 96 | "VrchJsonUrlLoaderNode": "JSON URL Loader @ vrch.ai", 97 | "VrchJsonWebSocketChannelLoaderNode": "JSON WebSocket Channel Loader @ vrch.ai", 98 | "VrchJsonWebSocketSenderNode": "JSON WebSocket Sender @ vrch.ai", 99 | "VrchMidiDeviceLoaderNode": "MIDI Device Loader @ vrch.ai", 100 | "VrchModelWebViewerNode": "3D MODEL Web Viewer @ vrch.ai", 101 | "VrchOSCControlSettingsNode": "OSC Control Settings @ vrch.ai", 102 | "VrchSwitchOSCControlNode": "SWITCH OSC Control @ vrch.ai", 103 | "VrchTextConcatOSCControlNode": "TEXT Concat OSC Control @ vrch.ai", 104 | "VrchTextKeyControlNode": "TEXT Key Control @ vrch.ai", 105 | "VrchTextSrtPlayerNode": "TEXT SRT Player @ vrch.ai", 106 | "VrchTextSwitchOSCControlNode": "TEXT Switch OSC Control @ vrch.ai", 107 | "VrchTriggerToggleNode": "Trigger Toggle @ vrch.ai", 108 | "VrchTriggerToggleX4Node": "Trigger Toggle x4 @ vrch.ai", 109 | "VrchTriggerToggleX8Node": "Trigger Toggle x8 @ vrch.ai", 110 | "VrchVideoWebViewerNode": "VIDEO Web Viewer @ vrch.ai", 111 | "VrchWebSocketServerNode": "WebSocket Server @ vrch.ai", 112 | "VrchWebViewerNode": "Web Viewer @ vrch.ai", 113 | "VrchXboxControllerNode": "Xbox Controller Mapper @ vrch.ai", 114 | "VrchXYOSCControlNode": "XY OSC Control @ vrch.ai", 115 | "VrchXYZOSCControlNode": "XYZ OSC Control @ vrch.ai", 116 | } 117 | 118 | # WEB_DIRECTORY is the comfyui nodes directory that ComfyUI will link and auto-load. 119 | WEB_DIRECTORY = "./web/comfyui" 120 | 121 | __all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS", "WEB_DIRECTORY"] 122 | 123 | -------------------------------------------------------------------------------- /assets/images/example_001_web_viewer.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/assets/images/example_001_web_viewer.gif -------------------------------------------------------------------------------- /assets/images/example_002_instant_queue_key_control.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/assets/images/example_002_instant_queue_key_control.gif -------------------------------------------------------------------------------- /assets/images/example_003_preview_image_in_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/assets/images/example_003_preview_image_in_background.png -------------------------------------------------------------------------------- /assets/images/sample_pic_01_woman_head.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/assets/images/sample_pic_01_woman_head.png -------------------------------------------------------------------------------- /assets/images/sample_pic_02_empty_room.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/assets/images/sample_pic_02_empty_room.png -------------------------------------------------------------------------------- /assets/images/sample_pic_03_hallway.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/assets/images/sample_pic_03_hallway.jpg -------------------------------------------------------------------------------- /assets/images/sample_pic_04_abstract_drawing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/assets/images/sample_pic_04_abstract_drawing.png -------------------------------------------------------------------------------- /assets/images/sample_pic_05_harley_quinn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/assets/images/sample_pic_05_harley_quinn.png -------------------------------------------------------------------------------- /assets/images/troubleshooting_001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/assets/images/troubleshooting_001.png -------------------------------------------------------------------------------- /assets/models/music_genre_cnn.pth: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:9c318c78ef39f78081b8cd3f9e84f268034813be889c089b84b314e4e0368ad8 3 | size 52840800 4 | -------------------------------------------------------------------------------- /assets/touchosc/comfyui_osc_control.tosc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/assets/touchosc/comfyui_osc_control.tosc -------------------------------------------------------------------------------- /assets/videos/intro.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/assets/videos/intro.mp4 -------------------------------------------------------------------------------- /assets/videos/intro_thumbnail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/assets/videos/intro_thumbnail.jpg -------------------------------------------------------------------------------- /docs/audio_nodes.md: -------------------------------------------------------------------------------- 1 | ### Node: `Audio Recorder @ vrch.ai` (vrch.ai/audio) 2 | 3 | 1. **Add the `Audio Recorder @ vrch.ai` node to your ComfyUI workflow.** 4 | 2. **Configure the Node:** 5 | - **Record Mode (`record_mode`):** 6 | - **"Press and Hold":** 7 | - **Button Control:** Hold the record button to start recording; release to stop. 8 | - **Shortcut Control:** Press and hold the selected shortcut key to start recording; release to stop. 9 | - **"Start and Stop":** 10 | - **Button Control:** Click "START" to begin recording; click "STOP" to end. 11 | - **Shortcut Control:** Press the selected shortcut key once to start recording; press again to stop. 12 | - **Recording Parameters:** 13 | - `record_duration_max`: Set the maximum recording duration (1-60 seconds). 14 | - `loop`: Enable or disable loop recording. 15 | - `loop_interval`: Set the interval between loop recordings (if loop is enabled). 16 | - `new_generation_after_recording`: Enable to automatically trigger a queue generation after recording. 17 | - **Shortcut Configuration:** 18 | - `shortcut`: Enable or disable the keyboard shortcut for controlling recording. 19 | - `shortcut_key`: Select the desired shortcut key (e.g., F1, F2, ..., F12) for controlling the recording. 20 | 3. **Record Audio:** 21 | - **"Press and Hold" Mode:** 22 | - **Button Control:** Hold the record button to record; release to stop. 23 | - **Shortcut Control:** Press and hold the selected shortcut key to record; release to stop. 24 | - **"Start and Stop" Mode:** 25 | - **Button Control:** Click "START" to begin recording; click "STOP" to end. 26 | - **Shortcut Control:** Press the selected shortcut key once to start recording; press again to stop. 27 | - **Loop Mode (Applicable in "Start and Stop" Mode):** 28 | - Click "START" to begin loop recording. 29 | - Click "STOP LOOPING" to end loop recording. 30 | 4. **Playback and Output:** 31 | - The recorded audio will appear in the `audioUI` widget for playback. 32 | - Use the `AUDIO` output to connect the recorded audio to other nodes in your workflow. 33 | 34 | **Note**: A countdown displays during the last 10 seconds of recording to inform you of the remaining time. 35 | 36 | --- 37 | 38 | ### Node: `Get Music Genres @ vrch.ai` (vrch.ai/audio) 39 | 40 | 1. **Add the `Get Music Genres @ vrch.ai` node to your ComfyUI workflow.** 41 | 2. **Configure the Node:** 42 | - **Audio Input (`audio`):** Provide an `AUDIO` input from a previous node in the workflow, such as an audio recorder or a file loader. 43 | 3. **Analyze Audio:** 44 | - The node processes the input audio to predict its music genre(s). 45 | - It uses a pre-trained model to analyze the waveform and outputs the predicted genres along with their probabilities. 46 | 4. **View Results:** 47 | - The predicted genres and their associated probabilities are displayed in a text output format. 48 | - The results indicate the likelihood of the audio belonging to specific music genres. 49 | 5. **Connect to Other Nodes:** 50 | - Use the `AUDIO` output to pass the analyzed audio to other nodes for further processing or playback. 51 | - Use the `STRING` output to connect the genre predictions to nodes that require textual input or visualization. 52 | 53 | **Note:** Ensure that the input audio is properly preprocessed and normalized for accurate genre prediction. The node's output is influenced by the quality and clarity of the input audio. 54 | 55 | --- 56 | 57 | ### Node: `Microphone Loader @ vrch.ai` (vrch.ai/audio) 58 | 59 | 1. **Add the `Microphone Loader @ vrch.ai` node to your ComfyUI workflow.** 60 | 2. **Configure the Node:** 61 | - **Sensitivity** (`sensitivity`): Adjust the microphone sensitivity level (0.0-1.0). 62 | - **Frame Size** (`frame_size`): Select audio frame size (256, 512, or 1024 samples). 63 | - **Sample Rate** (`sample_rate`): Choose sampling rate (16000, 24000, or 48000 Hz). 64 | - **Debug Mode** (`debug`): Enable to show raw data and debugging information. 65 | 3. **Connect to Microphone:** 66 | - The node will automatically detect available microphones. 67 | - Click "Refresh" to update the list of available devices. 68 | - Select a microphone from the dropdown menu to connect to it. 69 | 4. **Control the Microphone:** 70 | - Use the "Mute" button to silence/unmute the microphone. 71 | - The volume meter shows the current audio level. 72 | - Real-time waveform and spectrum visualizations display the audio characteristics. 73 | 5. **Outputs:** 74 | - `RAW_DATA`: Complete raw microphone data in JSON format. 75 | - `WAVEFORM`: Normalized audio waveform data as a float array. 76 | - `SPECTRUM`: Frequency spectrum data as a float array. 77 | - `VOLUME`: Current volume level (0.0-1.0). 78 | - `IS_ACTIVE`: Boolean indicating whether active sound is detected. 79 | 80 | **Applications:** 81 | - Voice activity detection for triggering actions in ComfyUI workflows 82 | - Audio-reactive visual effects 83 | - Sound level monitoring 84 | - Signal processing and analysis chains 85 | 86 | **Note:** For optimal performance, adjust the sensitivity based on your microphone and ambient noise conditions. 87 | 88 | --- 89 | 90 | ### Node: `AUDIO Concat @ vrch.ai` (vrch.ai/audio) 91 | 92 | 1. **Add the `AUDIO Concat @ vrch.ai` node to your ComfyUI workflow.** 93 | 2. **Configure the Node:** 94 | - **Audio Inputs:** 95 | - `audio1`: The first audio input to be concatenated. 96 | - `audio2`: The second audio input to be appended after the first. 97 | - **Crossfade Configuration:** 98 | - `crossfade_duration_ms`: The duration of the crossfade transition between audio1 and audio2, in milliseconds (0-10000). 99 | - **0:** No crossfade, audio files are simply joined together. 100 | - **> 0:** A gradual transition between the end of audio1 and the beginning of audio2. 101 | 3. **Audio Processing Features:** 102 | - **Sample Rate Handling:** Automatically resamples the second audio to match the first audio's sample rate if they differ. 103 | - **Channel Matching:** Ensures both audio inputs have compatible channel configurations. 104 | - **Crossfade:** Creates smooth transitions between audio segments when crossfade duration is set. 105 | 4. **Output:** 106 | - `AUDIO`: The concatenated audio that can be connected to other nodes in your workflow. 107 | 108 | **Applications:** 109 | - Creating continuous audio tracks from multiple recordings 110 | - Joining speech segments with background music 111 | - Building custom audio sequences for multimedia projects 112 | - Creating seamless loops by connecting the end of an audio clip to its beginning 113 | 114 | **Note:** For best results when using crossfade, ensure that both audio inputs have sufficient length for the crossfade duration. The crossfade will be limited to the shorter of the two audio segments if either is shorter than the specified crossfade duration. Remember that crossfade duration is specified in milliseconds (1000 milliseconds = 1 second). -------------------------------------------------------------------------------- /docs/gamepad_nodes.md: -------------------------------------------------------------------------------- 1 | ### Node: `Gamepad Loader @ vrch.ai` (vrch.ai/control/gamepad) 2 | 3 | 1. **Add the `Gamepad Loader @ vrch.ai` node to your ComfyUI workflow.** 4 | 5 | 2. **Configure the Node:** 6 | - **Gamepad Selection:** 7 | - **`index`**: Select the gamepad index (0-7) to use. Default is `0`. 8 | - **`name`**: Displays the detected gamepad's name (read-only). 9 | - **Performance Settings:** 10 | - **`refresh_interval`**: Set the data polling frequency in milliseconds (10-10000ms). Default is `100`. 11 | - **Debug Options:** 12 | - **`debug`**: Enable to view detailed debug information in the console and display raw data. Default is `False`. 13 | - **`raw_data`**: Shows the raw gamepad data in JSON format (only visible when debug is enabled). 14 | 15 | 3. **Connecting a Gamepad:** 16 | - Connect a compatible gamepad to your computer before starting ComfyUI. 17 | - Most USB and Bluetooth gamepads compatible with your browser should work. 18 | - Press buttons on your gamepad to ensure it's detected by the browser. 19 | 20 | 4. **Data Access:** 21 | - The node continuously polls the connected gamepad at the specified refresh interval. 22 | - It extracts and processes the gamepad's buttons and axes data. 23 | - When a gamepad is detected at the specified index, its identifier will appear in the `name` field. 24 | 25 | 5. **Node Outputs:** 26 | - **`RAW_DATA`**: Complete gamepad data in JSON format. 27 | - **`LEFT_STICK`**: Array containing X and Y values for the left analog stick `[x, y]` (values range from -1.0 to 1.0). 28 | - **`RIGHT_STICK`**: Array containing X and Y values for the right analog stick `[x, y]` (values range from -1.0 to 1.0). 29 | - **`BOOLEAN_BUTTONS`**: Array of boolean values indicating if each button is pressed (`True`) or not (`False`). 30 | - **`INT_BUTTONS`**: Array of integer values (1 for pressed, 0 for not pressed). 31 | - **`FLOAT_BUTTONS`**: Array of float values representing the pressure sensitivity of each button (0.0-1.0). 32 | 33 | **Note:** 34 | - Gamepad support varies by browser. Chrome and Edge typically provide the best compatibility. 35 | - The number of buttons and axes depends on your specific gamepad model. 36 | - Standard gamepads typically follow this mapping: 37 | - Buttons 0-3: Face buttons (A/B/X/Y or ×/○/□/△) 38 | - Buttons 4-5: Shoulder buttons (LB/RB) 39 | - Buttons 6-7: Triggers (LT/RT) 40 | - Buttons 8-9: Back/Select and Start buttons 41 | - Buttons 10-11: Analog stick press buttons (L3/R3) 42 | - Buttons 12-15: D-pad (Up/Down/Left/Right) 43 | - Axes 0-1: Left analog stick (X, Y) 44 | - Axes 2-3: Right analog stick (X, Y) 45 | 46 | ### Node: `Xbox Controller Mapper @ vrch.ai` (vrch.ai/control/gamepad) 47 | 48 | 1. **Overview:** 49 | - The `Xbox Controller Mapper @ vrch.ai` node provides optimized mapping for Xbox controllers. 50 | - It takes raw gamepad data from the `Gamepad Loader @ vrch.ai` node and maps it to standard Xbox controller inputs. 51 | - This makes it easier to work with Xbox controllers by providing named outputs for each button and control. 52 | 53 | 2. **Setup:** 54 | - First add a `Gamepad Loader @ vrch.ai` node to your workflow. 55 | - Then add the `Xbox Controller Mapper @ vrch.ai` node. 56 | - Connect the `RAW_DATA` output from the Gamepad Loader to the `raw_data` input of the Xbox Controller Mapper. 57 | 58 | 3. **Inputs:** 59 | - **`raw_data`**: JSON data from the `Gamepad Loader @ vrch.ai` node. 60 | - **`debug`**: Enable to view detailed debug information in the console. Default is `False`. 61 | 62 | 4. **Outputs:** 63 | - **Analog Controls:** 64 | - **`LEFT_STICK`**: Array containing X and Y values for the left analog stick `[x, y]` (values range from -1.0 to 1.0). 65 | - **`RIGHT_STICK`**: Array containing X and Y values for the right analog stick `[x, y]` (values range from -1.0 to 1.0). 66 | - **`LEFT_TRIGGER`**: Value for the left trigger (LT) (values range from 0.0 to 1.0). 67 | - **`RIGHT_TRIGGER`**: Value for the right trigger (RT) (values range from 0.0 to 1.0). 68 | 69 | - **Face Buttons:** 70 | - **`A_BUTTON`**: Boolean state of the A button (bottom face button). 71 | - **`B_BUTTON`**: Boolean state of the B button (right face button). 72 | - **`X_BUTTON`**: Boolean state of the X button (left face button). 73 | - **`Y_BUTTON`**: Boolean state of the Y button (top face button). 74 | 75 | - **Shoulder Buttons:** 76 | - **`LB_BUTTON`**: Boolean state of the left bumper (LB). 77 | - **`RB_BUTTON`**: Boolean state of the right bumper (RB). 78 | 79 | - **Center Buttons:** 80 | - **`VIEW_BUTTON`**: Boolean state of the View button (formerly Back). 81 | - **`MENU_BUTTON`**: Boolean state of the Menu button (formerly Start). 82 | - **`XBOX_BUTTON`**: Boolean state of the Xbox logo button. 83 | 84 | - **Stick Presses:** 85 | - **`LEFT_STICK_PRESS`**: Boolean state of the left stick press (L3). 86 | - **`RIGHT_STICK_PRESS`**: Boolean state of the right stick press (R3). 87 | 88 | - **D-Pad:** 89 | - **`DPAD_UP`**: Boolean state of the D-pad up direction. 90 | - **`DPAD_DOWN`**: Boolean state of the D-pad down direction. 91 | - **`DPAD_LEFT`**: Boolean state of the D-pad left direction. 92 | - **`DPAD_RIGHT`**: Boolean state of the D-pad right direction. 93 | 94 | - **Complete Data:** 95 | - **`FULL_MAPPING`**: Complete mapping of all controller inputs as a JSON object. 96 | 97 | 5. **Usage Examples:** 98 | - Use `A_BUTTON` to trigger actions in your workflow. 99 | - Use `LEFT_STICK` to control position or movement parameters. 100 | - Use `RIGHT_TRIGGER` to control intensity or pressure-sensitive parameters. 101 | 102 | **Note:** 103 | - This node is specifically optimized for Xbox controllers and follows the standard Xbox controller button layout. 104 | - The button mapping is based on the standard Web Gamepad API implementation for Xbox controllers. 105 | - If your controller has a different layout, you may need to adjust your workflow accordingly. -------------------------------------------------------------------------------- /docs/image_nodes.md: -------------------------------------------------------------------------------- 1 | ### Node: `IMAGE Saver @ vrch.ai` (vrch.ai/image) 2 | 3 | 1. **Add the `IMAGE Saver @ vrch.ai` node to your ComfyUI workflow.** 4 | 5 | 2. **Configure the Node:** 6 | - **Image Input:** 7 | - **`images`**: Provide one or more images to be saved. 8 | - **Filename:** 9 | - **`filename`**: Specify the base filename for saving the image(s). If multiple images are provided, an index will be appended (e.g., `web_viewer_image_00.jpeg`). 10 | - **Path:** 11 | - **`path`**: Define the subfolder within the ComfyUI output directory where the images will be saved (default is `web_viewer`). 12 | - **File Extension:** 13 | - **`extension`**: Choose the output image format from **`jpeg`**, **`png`**, or **`webp`** (default is **`jpeg`**). 14 | - **Quality Settings:** 15 | - **`quality_jpeg_or_webp`**: Set the quality for JPEG or WEBP images (default is **85**, valid range: **1–100**). 16 | - **`optimize_png`**: Enable PNG optimization when saving PNG images. 17 | - **`lossless_webp`**: Enable lossless compression for WEBP images. 18 | - **Preview Option:** 19 | - **`enable_preview`**: If enabled, the node will return preview information for the saved images. 20 | 21 | 3. **Save Images:** 22 | - The node saves the provided images into the specified subfolder under the ComfyUI output directory using the given filename and file extension. 23 | - When multiple images are provided, filenames will be automatically suffixed with an index. 24 | 25 | --- 26 | 27 | ### Node: `IMAGE Preview in Background (Legacy) @ vrch.ai` (vrch.ai/image) 28 | 29 | ![](../assets/images/example_003_preview_image_in_background.png) 30 | 31 | #### 1. Adding the Node 32 | Add the `IMAGE Preview in Background (Legacy) @ vrch.ai` node to your ComfyUI workflow. 33 | 34 | #### 2. Node Configuration 35 | - **Image Input:** 36 | - **`images`**: Provide a single image to be used for the background preview. 37 | - **Channel:** 38 | - **`channel`**: Select a channel number from **"1"** to **"8"** (default is **"1"**). This determines the filename prefix (e.g., `channel_1.jpeg` or in batch mode `channel_1_00.jpeg`). 39 | - **Background Display:** 40 | - **`background_display`**: Toggle to enable or disable displaying the image as a background in ComfyUI. 41 | - **Refresh Interval:** 42 | - **`refresh_interval_ms`**: Set the interval (in milliseconds) for refreshing the background preview (default is **300ms**). 43 | - **Display Option:** 44 | - **`display_option`**: Choose how the image is displayed in the background. Options include **`original`**, **`fit`**, **`stretch`**, and **`crop`** (default is **`fit`**). 45 | - **Batch Display:** 46 | - **`batch_display`**: Toggle to enable batch display mode. When enabled, the node will cycle through multiple images within a batch. 47 | - **Batch Display Interval:** 48 | - **`batch_display_interval_ms`**: Set the interval (in milliseconds) for switching between images in batch mode (default is **200ms**). 49 | - **Batch Images Size:** 50 | - **`batch_images_size`**: Define the total number of images in the batch, which determines the range for the index used in the filename (default is **4**). 51 | 52 | #### 3. Background Preview 53 | - When **`background_display`** is enabled, the node saves the input image to the `preview_background` subfolder within the ComfyUI output directory. 54 | - **File Naming Rules**: 55 | - If **`batch_display`** is **false**, the saved filename format is: 56 | `channel_{channel}.jpeg` 57 | - If **`batch_display`** is **true**, the saved filename format is: 58 | `channel_{channel}_{index:02d}.jpeg` 59 | where **`{index}`** is updated cyclically based on **`batch_display_interval_ms`** and within the range defined by **`batch_images_size`** (for example: `channel_1_00.jpeg`, `channel_1_01.jpeg`, `channel_1_02.jpeg`, `channel_1_03.jpeg`). 60 | 61 | #### 4. Notes 62 | - Ensure that the output directory is correctly configured, as the node saves images into the `preview_background` folder within that directory. 63 | - The node dynamically updates the background preview by refreshing the saved image at the set interval. Adjust the refresh interval and batch display parameters as needed for optimal preview performance. 64 | 65 | --- 66 | 67 | ### Node: `IMAGE Preview in Background @ vrch.ai` (vrch.ai/image) 68 | 69 | This node sends image data directly to the ComfyUI front‑end without saving files, enabling low‑latency background previews. 70 | 71 | #### 1. Adding the Node 72 | Add the `IMAGE Preview in Background @ vrch.ai` node to your ComfyUI workflow. 73 | 74 | #### 2. Node Configuration 75 | - **`images`**: Provide one or more images to be previewed in the background. 76 | - **`background_display`**: Toggle to enable or disable the background preview. 77 | - **`batch_display`**: Toggle to enable batch mode; when enabled, all provided images will cycle in the background. 78 | - **`batch_display_interval_ms`**: Interval (ms) between image switches when batch display is enabled. 79 | - **`display_option`**: How the image is rendered. Options: 80 | - `original`: draw at native resolution, centered 81 | - `fit`: scale to fit canvas, preserving aspect ratio 82 | - `stretch`: fill entire canvas, distorting aspect ratio 83 | - `crop`: scale to fill canvas, cropping overflow 84 | 85 | #### 3. Behavior 86 | - When **`background_display`** is enabled, images are sent as Base64‑encoded strings in the `ui.images` field. 87 | - If **`batch_display`** is **false**, only the first image is shown. 88 | - The **`display_option`** value is sent via `ui.display_option` and controls the canvas scaling mode. 89 | 90 | #### 4. Notes 91 | - No disk I/O required; previews are delivered through the UI payload. 92 | - Batch mode loops through all images automatically at the specified interval. -------------------------------------------------------------------------------- /docs/key_control_nodes.md: -------------------------------------------------------------------------------- 1 | ### Node: `INT Key Control @ vrch.ai` (vrch.ai/control/keyboard) 2 | 3 | 1. **Add the `INT Key Control @ vrch.ai` to your ComfyUI workflow.** 4 | 2. **Configure the Node:** 5 | - **Minimum Value (`min_value`):** Set the minimum allowable integer value (integer between `-9999` and `9999`). Default is `0`. 6 | - **Maximum Value (`max_value`):** Set the maximum allowable integer value (integer between `-9999` and `9999`). Default is `100`. 7 | - **Step Size (`step_size`):** Set the increment/decrement value (integer between `1-10`). Default is `1`. 8 | - **Shortcut Key 1 (`shortcut_key1`):** Select a key from `F1` to `F12` to serve as the primary shortcut key. Default is `F2`. 9 | - **Shortcut Key 2 (`shortcut_key2`):** Choose between `"Down/Up"` or `"Left/Right"` to determine the direction keys. Default is `"Down/Up"`. 10 | - **Current Value (`current_value`):** Set the initial integer value within the specified range (integer between `-9999` and `9999`). Default is `50`. 11 | 3. **Control Integer Value:** 12 | - **Incrementing:** 13 | - Press and hold the selected `shortcut_key1` (e.g., `F2`). 14 | - While holding `shortcut_key1`, press `ArrowUp` or `ArrowRight` to increment. 15 | - The `current_value` will increase by `step_size` each time, up to the defined `max_value`. 16 | - **Decrementing:** 17 | - Press and hold the selected `shortcut_key1` (e.g., `F2`). 18 | - While holding `shortcut_key1`, press `ArrowDown` or `ArrowLeft` to decrement. 19 | - The `current_value` will decrease by `step_size` each time, down to the defined `min_value`. 20 | 4. **Display and Output:** 21 | - The current integer value is displayed within the node's UI. 22 | - Use the `INT` output to connect the integer value to other nodes in your workflow. 23 | 24 | **Note:** 25 | - Ensure that the ComfyUI window/tab is focused when using keyboard shortcuts. 26 | - Prevent browser-specific shortcuts from interfering with the node's functionality. 27 | - Modified `min_value`, `max_value`, and `step_size` values persist across page reloads. 28 | - The `current_value` will always stay within the defined `min_value` and `max_value` boundaries, regardless of user interactions. 29 | 30 | --- 31 | 32 | ### Node: `FLOAT Key Control @ vrch.ai` (vrch.ai/control/keyboard) 33 | 34 | 1. **Add the `FLOAT Key Control @ vrch.ai` to your ComfyUI workflow.** 35 | 2. **Configure the Node:** 36 | - **Step Size (`step_size`):** Set the increment/decrement value (float between `0.01-0.10`). Default is `0.01`. 37 | - **Shortcut Key 1 (`shortcut_key1`):** Select a key from `F1` to `F12` to serve as the primary shortcut key. Default is `F1`. 38 | - **Shortcut Key 2 (`shortcut_key2`):** Choose between `"Down/Up"` or `"Left/Right"` to determine the direction keys. Default is `"Down/Up"`. 39 | - **Current Value (`current_value`):** Set the initial floating-point value (between `0.0-1.0`). Default is `0.50`. 40 | 3. **Control Floating-Point Value:** 41 | - **Incrementing:** 42 | - Press and hold the selected `shortcut_key1` (e.g., `F1`). 43 | - While holding `shortcut_key1`, press the corresponding direction key based on `shortcut_key2`: 44 | - If `"Down/Up"`: Press `ArrowUp` to increment. 45 | - If `"Left/Right"`: Press `ArrowLeft` to increment. 46 | - The `current_value` will increase by `step_size` each time, up to a maximum of `1.0`. 47 | - **Decrementing:** 48 | - Press and hold the selected `shortcut_key1` (e.g., `F1`). 49 | - While holding `shortcut_key1`, press the corresponding direction key based on `shortcut_key2`: 50 | - If `"Down/Up"`: Press `ArrowDown` to decrement. 51 | - If `"Left/Right"`: Press `ArrowRight` to decrement. 52 | - The `current_value` will decrease by `step_size` each time, down to a minimum of `0.0`. 53 | 4. **Display and Output:** 54 | - The current floating-point value is displayed within the node's UI. 55 | - Use the `FLOAT` output to connect the floating-point value to other nodes in your workflow. 56 | 57 | **Note:** Ensure that the ComfyUI window/tab is focused when using keyboard shortcuts. Prevent browser-specific shortcuts from interfering with the node's functionality. 58 | 59 | --- 60 | 61 | ### Node: `BOOLEAN Key Control @ vrch.ai` (vrch.ai/control/keyboard) 62 | 63 | 1. **Add the `BOOLEAN Key Control @ vrch.ai` to your ComfyUI workflow.** 64 | 2. **Configure the Node:** 65 | - **Shortcut Key (`shortcut_key`):** Select a key from `F1` to `F12` to serve as the toggle shortcut. Default is `F1`. 66 | - **Current Value (`current_value`):** Set the initial boolean value (`True`/`False`). Default is `False`. 67 | 3. **Toggle Boolean Value:** 68 | - **Using Shortcut Key:** 69 | - Press the selected `shortcut_key` (e.g., `F1`) to toggle the `current_value` between `True` and `False`. 70 | - Each press of the `shortcut_key` will switch the state. 71 | 4. **Display and Output:** 72 | - The current boolean value is displayed within the node's UI. 73 | - Use the `BOOL` output to connect the boolean value to other nodes in your workflow. 74 | 75 | **Note:** Ensure that the ComfyUI window/tab is focused when using the shortcut key. Prevent browser-specific shortcuts from interfering with the node's functionality. 76 | 77 | --- 78 | 79 | ### Node: `TEXT Key Control @ vrch.ai` (vrch.ai/control/keyboard) 80 | 81 | 1. **Add the `TEXT Key Control @ vrch.ai` node to your ComfyUI workflow.** 82 | 2. **Configure the Node:** 83 | - **Text Inputs (`text1` - `text8`):** Enter text for each option. Supports multiple lines. Defaults are empty (`""`). 84 | - **Jump Empty Option (`skip_empty_option`):** Enable or disable skipping empty text options when cycling. Default is `True`. 85 | - **Shortcut Key (`shortcut_key`):** Select a function key (`F1` to `F12`) to cycle through texts. Default is `F2`. 86 | - **Current Value (`current_value`):** Set the initial selection (`1` to `8`). Default is `1`. 87 | - **Auto Switch (`enable_auto_switch`):** Enable or disable auto switch. Default is `False`. 88 | - **Auto Switch Delay (ms) (`auto_switch_delay_ms`):** Delay in milliseconds for auto-switching. 89 | 3. **Cycle Through Text Options:** 90 | - **Using Shortcut Key:** 91 | - Press the selected `shortcut_key` (e.g., `F2`) to cycle through the text options. 92 | - **With `skip_empty_option` Enabled (`True`):** 93 | - Skips any empty `text` inputs. 94 | - **With `skip_empty_option` Disabled (`False`):** 95 | - Cycles through all texts, including empty ones. 96 | 4. **Display and Output:** 97 | - **Display:** 98 | - Shows `Value: X`, where `X` is the current selection (`1` to `8`). 99 | - **Output:** 100 | - **Type:** `STRING` 101 | - Outputs the selected text based on `current_value`. Connect to other nodes as needed. 102 | 103 | **Note:** 104 | Ensure the ComfyUI window/tab is focused when using the shortcut key to prevent conflicts with browser shortcuts. 105 | 106 | ---- 107 | 108 | ### Node: `Instant Queue Key Control @ vrch.ai` (vrch.ai/control/keyboard) 109 | 110 | ![Example of Instant Queue Key Control](../assets/images/example_002_instant_queue_key_control.gif) 111 | 112 | 1. **Add the `Instant Queue Key Control @ vrch.ai` to your ComfyUI workflow.** 113 | 114 | 2. **Configure the Node:** 115 | - **Queue Option (`queue_option`):** Options of the initial state of the queue status, including `once`, `instant` and `change`. Default is `instant`. 116 | - **Shortcut Key (`shortcut_key`):** Select a function key (`F1` to `F12`) as the shortcut key to switch the queue option from one to another. Default is `F2`. 117 | - **Enable Queue Autorun (`enable_queue_autorun`):** Toggle whether the queue will automatically run after a delay when activated. Default is `False`. 118 | - **Autorun Delay (`autorun_delay`):** Sets the delay (in seconds) before the queue automatically triggers when `enable_queue_autorun` is enabled. The value ranges from 1 to 60 seconds, with a default of 5 seconds. 119 | 120 | 3. **Toggle Queue Option:** 121 | - **Using Shortcut Key:** 122 | - Pressing the chosen `shortcut_key` (e.g., `F2`) toggles the `queue_option` option between `once`, `instant` and `change`. 123 | - Each press of the shortcut key switches the state, allowing for easy control over the instant queue function without manually adjusting the `queue_option` setting. 124 | 125 | 4. **Display and Output:** 126 | - The node displays the current status of the instant queue (either **Enabled** or **Disabled**) within its UI. 127 | - If `enable_queue_autorun` is enabled, a countdown timer displays the time remaining until the queue runs, updating every second. 128 | - No additional outputs are provided, as this node functions solely as a control toggle within the workflow. 129 | 130 | 5. **Interruptible Countdown:** 131 | - If the queue is re-triggered during an active countdown, the existing countdown will reset, and a new countdown will start from the configured `autorun_delay` time. This ensures only one activation cycle runs at a time. 132 | 133 | **Note:** 134 | Ensure the ComfyUI window/tab is focused when using the shortcut key to avoid conflicts with browser-specific shortcuts or system key bindings. 135 | 136 | --- 137 | -------------------------------------------------------------------------------- /docs/logic_nodes.md: -------------------------------------------------------------------------------- 1 | ### Node: `INT Remap @ vrch.ai` (vrch.ai/logic) 2 | 3 | 1. **Add the `INT Remap @ vrch.ai` node to your ComfyUI workflow.** 4 | 5 | 2. **Configure the Node:** 6 | - **Optional Input:** 7 | - **`input`**: The integer value to remap. Connect an INT input or leave unconnected for the default. 8 | - **Required Parameters:** 9 | - **`input_min`**: Minimum of the input range (default: 0). 10 | - **`input_max`**: Maximum of the input range (default: 1). 11 | - **`output_min`**: Minimum of the output range (default: 0). 12 | - **`output_max`**: Maximum of the output range (default: 100). 13 | - **`output_invert`**: Toggle to invert the mapping (default: False). 14 | - **`output_default`**: Default output if mapping fails (default: 0). 15 | 16 | 3. **Remapping Behavior:** 17 | - Ensures `input_min <= input_max` and `output_min <= output_max`, otherwise raises an error. 18 | - Maps the input proportionally from the input range to the output range. 19 | - Clamps the result within `[output_min, output_max]` and casts to INT. 20 | - On any exception, outputs `output_default`. 21 | 22 | 4. **Node Output:** 23 | - **Output (INT):** The remapped integer value. 24 | 25 | --- 26 | 27 | ### Node: `FLOAT Remap @ vrch.ai` (vrch.ai/logic) 28 | 29 | 1. **Add the `FLOAT Remap @ vrch.ai` node to your ComfyUI workflow.** 30 | 31 | 2. **Configure the Node:** 32 | - **Optional Input:** 33 | - **`input`**: The float value to remap. Connect a FLOAT input or leave unconnected for the default. 34 | - **Required Parameters:** 35 | - **`input_min`**: Minimum of the input range (default: 0.0). 36 | - **`input_max`**: Maximum of the input range (default: 1.0). 37 | - **`output_min`**: Minimum of the output range (default: 0.0). 38 | - **`output_max`**: Maximum of the output range (default: 100.0). 39 | - **`output_invert`**: Toggle to invert the mapping (default: False). 40 | - **`output_default`**: Default output if mapping fails (default: 0.0). 41 | 42 | 3. **Remapping Behavior:** 43 | - Validates ranges (`input_min <= input_max` and `output_min <= output_max`). 44 | - Maps the input proportionally, clamps to `[output_min, output_max]`. 45 | - Returns a FLOAT. On error, outputs `output_default`. 46 | 47 | 4. **Node Output:** 48 | - **Output (FLOAT):** The remapped float value. 49 | 50 | --- 51 | 52 | ### Node: `Trigger Toggle @ vrch.ai` (vrch.ai/logic) 53 | 54 | 1. **Add the `Trigger Toggle @ vrch.ai` node to your ComfyUI workflow.** 55 | 56 | 2. **Configure the Node:** 57 | - **Optional Input:** 58 | - **`trigger`**: BOOLEAN trigger input; toggles state when True. 59 | - **Required Parameters:** 60 | - **`initial_state`**: Starting boolean state (default: False). 61 | - **`debug`**: Enable console logging of transitions (default: False). 62 | 63 | 3. **Toggle Behavior:** 64 | - If `initial_state` changes, resets the internal state accordingly. 65 | - On each True `trigger` event, flips the current state. 66 | - If `debug` is enabled, prints trigger, initial, and current states. 67 | 68 | 4. **Node Output:** 69 | - **Output (BOOLEAN):** The current toggle state. 70 | - **JSON (JSON):** A JSON object containing `trigger`, `initial_state`, and `current_state`. 71 | 72 | 5. **Visual Indicators:** 73 | - Displays a non‑clickable circular indicator in the UI reflecting the current state. 74 | 75 | --- 76 | 77 | ### Node: `Trigger Toggle x4 @ vrch.ai` (vrch.ai/logic) 78 | 79 | 1. **Add the `Trigger Toggle x4 @ vrch.ai` node to your ComfyUI workflow.** 80 | 81 | 2. **Configure the Node:** 82 | - **Optional Inputs:** 83 | - **`trigger1`**, **`trigger2`**, **`trigger3`**, **`trigger4`**: Four BOOLEAN triggers. 84 | - **Required Parameters:** 85 | - **`initial_state1`** … **`initial_state4`**: Four starting states (default: False). 86 | - **`debug`**: Enable console logging (default: False). 87 | 88 | 3. **Toggle Behavior:** 89 | - Resets each channel when its initial state changes. 90 | - Toggles individual channel states on corresponding triggers. 91 | - Builds a JSON summary of all four channels. 92 | - If `debug` is enabled, prints the JSON summary. 93 | 94 | 4. **Node Outputs:** 95 | - **Output1–Output4 (BOOLEAN):** Current states for each channel. 96 | - **JSON (JSON):** JSON object grouping triggers, initial, and current states for all channels. 97 | 98 | 5. **Visual Indicators:** 99 | - Renders four non‑clickable circular indicators in the UI, one per channel, showing each current state. 100 | 101 | --- 102 | 103 | ### Node: `Trigger Toggle x8 @ vrch.ai` (vrch.ai/logic) 104 | 105 | 1. **Add the `Trigger Toggle x8 @ vrch.ai` node to your ComfyUI workflow.** 106 | 107 | 2. **Configure the Node:** 108 | - **Optional Inputs:** 109 | - **`trigger1` … `trigger8`**: Eight BOOLEAN triggers. 110 | - **Required Parameters:** 111 | - **`initial_state1` … `initial_state8`**: Eight starting states (default: False). 112 | - **`debug`**: Enable console logging (default: False). 113 | 114 | 3. **Toggle Behavior:** 115 | - Resets each channel on initial state change. 116 | - Toggles individual states on corresponding triggers. 117 | - Builds a JSON summary of all eight channels. 118 | - If `debug` is enabled, prints the JSON summary. 119 | 120 | 4. **Node Outputs:** 121 | - **Output1–Output8 (BOOLEAN):** Current states for each channel. 122 | - **JSON (JSON):** JSON object grouping triggers, initial, and current states for all channels. 123 | 124 | 5. **Visual Indicators:** 125 | - Renders eight non‑clickable circular indicators in the UI, one per channel, showing each current state. 126 | -------------------------------------------------------------------------------- /docs/midi_nodes.md: -------------------------------------------------------------------------------- 1 | ### Node: `MIDI Device Loader @ vrch.ai` (vrch.ai/control/midi) 2 | 3 | 1. **Add the `MIDI Device Loader @ vrch.ai` node to your ComfyUI workflow.** 4 | 5 | 2. **Configure the Node:** 6 | - **`device_id`**: Select the MIDI device ID to use. Default is empty (connects to first available device). 7 | - **`name`**: Displays the connected MIDI device name (read-only). 8 | - **`refresh_interval`**: Set the polling frequency in milliseconds (10–10000ms). Default is `100`. 9 | - **`debug`**: Enable to view detailed debug information in the console and display raw data. Default is `False`. 10 | - **`raw_data`**: Shows the raw MIDI state in JSON format (visible only when debug is enabled). 11 | 12 | 3. **Connecting a MIDI Device:** 13 | - Connect a compatible MIDI device to your computer before starting ComfyUI. 14 | - Ensure your browser supports the Web MIDI API (Chrome and Edge have the best support). 15 | - The node will automatically connect to the first available device if `device_id` is not specified. 16 | - To target a specific device, set the `device_id` field to the device’s ID and the node will reconnect if that device appears. 17 | 18 | 4. **Data Access:** 19 | - The node polls the connected MIDI device at the specified `refresh_interval`. 20 | - Incoming MIDI messages are parsed into CC (Control Change) and Note events. 21 | - CC and Note data are stored in arrays of length 128 (one slot per MIDI number). 22 | 23 | 5. **Node Outputs:** 24 | - **`RAW_DATA`**: Complete MIDI state in JSON format. 25 | - **`INT_CC_VALUES`**: Array of integer CC values (0–127). 26 | - **`FLOAT_CC_VALUES`**: Array of normalized CC values (0.0–1.0). 27 | - **`BOOLEAN_NOTE_VALUES`**: Array of booleans indicating note on (`True`) or off (`False`) for each MIDI note number. 28 | - **`INT_NOTE_VALUES`**: Array of integer note velocity values (0–127). 29 | 30 | **Note:** 31 | - Supported message types include Note On/Off, Control Change, Aftertouch, Program Change, Channel Pressure, and Pitch Bend. 32 | - High-end MIDI controllers may send more message types; unsupported types are ignored by default. 33 | - Multiple devices can be handled by adding multiple `MIDI Device Loader` nodes with different `device_id` settings. -------------------------------------------------------------------------------- /docs/text_nodes.md: -------------------------------------------------------------------------------- 1 | ### Node: `JSON URL Loader @ vrch.ai` (vrch.ai/text) 2 | 3 | 1. **Add the `JSON URL Loader @ vrch.ai` node to your ComfyUI workflow.** 4 | 5 | 2. **Configure the Node:** 6 | - **URL:** 7 | - **`url`**: Provide the URL from which the node will load JSON content. 8 | - **Optional Settings:** 9 | - **`print_to_console`**: Toggle this option to print the loaded JSON to the console for debugging purposes. 10 | 11 | 3. **Load JSON:** 12 | - The node sends a GET request to the specified URL with a 5-second timeout. 13 | - Upon success, it parses the response into a JSON object. 14 | - In case of a request failure or if the JSON is invalid, the node outputs an empty JSON object (`{}`). 15 | - If enabled, the node prints a formatted version of the JSON to the console for inspection. 16 | 17 | **Note:** 18 | - Ensure that the URL is accessible and returns valid JSON data. 19 | 20 | 21 | --- 22 | 23 | ### Node: `TEXT SRT Player @ vrch.ai` (vrch.ai/text) 24 | 25 | 1. **Add the `TEXT SRT Player @ vrch.ai` node to your ComfyUI workflow.** 26 | 27 | 2. **Configure the Node:** 28 | - **SRT Text Input:** 29 | - **`srt_text`**: Provide the SRT-formatted text containing subtitle timing and content. The text should follow proper SRT formatting. 30 | - **Placeholder Text:** 31 | - **`placeholder_text`**: Specify the text that will be output when no subtitle is active (i.e., when `current_selection` is set to `-1`). 32 | - **Loop:** 33 | - **`loop`**: Toggle this option to enable or disable looping playback of the SRT file. 34 | - **Current Selection:** 35 | - **`current_selection`**: This integer value is automatically updated during playback to indicate the currently active subtitle. When the playback time is outside any subtitle interval, the value is set to `-1`, causing the placeholder text to be output instead. 36 | - **Debug:** 37 | - **`debug`**: Enable debug mode to print detailed parsing and playback logs to the console (useful for troubleshooting or verifying SRT parsing). 38 | 39 | 3. **Play SRT Text & Interactive Controls:** 40 | - **Subtitle Parsing & Playback:** 41 | - The node uses an SRT parsing library to convert the provided SRT text into a list of subtitle entries. 42 | - As the node “plays” the subtitles, it continuously updates `current_selection` based on the current playback time. 43 | - **Play/Pause Button:** 44 | - Toggles between playing and pausing the subtitle playback. The button text updates to reflect the current state. 45 | - **Reset Button:** 46 | - Resets the playback time to the beginning (0 seconds). 47 | - **Playback Time Display:** 48 | - Shows the current playback time (in seconds) and updates in real time while subtitles are playing. 49 | - **Progress Slider:** 50 | - An interactive timeline bar for jumping to a specific time within the SRT file. 51 | - Dragging the slider updates the playback time accordingly, and the displayed subtitle changes if a new subtitle range is entered. 52 | - **Looping:** 53 | - If `loop` is enabled, playback automatically restarts at 0 seconds after the last subtitle entry ends. 54 | 55 | 4. **Node Output:** 56 | - The node outputs the text of the currently active subtitle entry. If the playback time is not within any subtitle range, the node outputs the `placeholder_text`. 57 | - You can chain this output into subsequent nodes in your ComfyUI workflow for further processing or display. 58 | 59 | **Note:** 60 | - Ensure that your SRT text is correctly formatted (with sequential numbering, time range lines, and subtitle text). -------------------------------------------------------------------------------- /docs/tutorial_001_live_portrait_with_gamepad.md: -------------------------------------------------------------------------------- 1 | ## TL;DR 2 | 3 | If you saw the [last post on real-time avatar control with ComfyUI and Vision Pro](https://www.reddit.com/r/comfyui/comments/1fyokln/update_realtime_avatar_control_with_comfyui_and/), here’s the next piece: a fully open-source guide to adding gamepad integration. 🎮 4 | 5 | With this setup, you can use your gamepad to control live portrait movements and expressions in ComfyUI. It’s all about bringing that next level of interactive control into your setup—perfect for adding expressions, head movement, and more to your avatar with just a joystick and buttons. Follow along and give it a try! 6 | 7 | ![](../example_workflows/example_osc_control_002_live_portrait_with_gamepad.png) 8 | 9 | ## Preparations 10 | 11 | 1. Install `ComfyUI Web Viewer` custome node: 12 | - Method 1: search for `ComfyUI Web Viewer` in ComfyUI Manager 13 | - Method 2: install from github: [https://github.com/VrchStudio/comfyui-web-viewer](https://github.com/VrchStudio/comfyui-web-viewer) 14 | 2. Install `Advanced Live Portrait` custome node: 15 | - Method 1: search for `ComfyUI-AdvancedLivePortrait` in ComfyUI Manager 16 | - Method 2: install from github: [https://github.com/PowerHouseMan/ComfyUI-AdvancedLivePortrait](https://github.com/PowerHouseMan/ComfyUI-AdvancedLivePortrait) 17 | 3. Download `Workflow Example: Live Portrait + Gamepad` workflow: 18 | - Download it from here: [example_osc_control_002_live_portrait_with_gamepad.json](https://github.com/VrchStudio/comfyui-web-viewer/blob/main/example_workflows/example_osc_control_002_live_portrait_with_gamepad.json) 19 | 4. Install `TouchOSC` app 20 | - AppStore: [https://apps.apple.com/us/app/touchosc/id1569996730](https://apps.apple.com/us/app/touchosc/id1569996730) 21 | - Official Website: [https://hexler.net/touchosc](https://hexler.net/touchosc) 22 | 5. Download `comfyui_osc_control.tosc` comfyui osc control panel in `TouchOSC` app 23 | - Download it from here: [comfyui_osc_control.tosc](https://github.com/VrchStudio/comfyui-web-viewer/blob/main/assets/touchosc/comfyui_osc_control.tosc) 24 | 25 | ## How to play 26 | 27 | ### Using Gamepad in TouchOSC App 28 | 29 | 1. **Connect Your Gamepad**: Connect your gamepad to the device running the `TouchOSC` app (usually through Bluetooth). 30 | 2. **Open TouchOSC**: Open `TouchOSC` app on your device. 31 | 3. **Set Up Connections in TouchOSC**: 32 | - Go to **Connections** settings in `TouchOSC` app. 33 | - For **OSC**, enter your ComfyUI server’s IP address (e.g., `192.168.1.100:8000`). 34 | - For **Gamepad**, enable the Gamepad connection feature. 35 | 4. **Import the OSC Control Panel File**: Add the [comfyui_osc_control.tosc](https://github.com/VrchStudio/comfyui-web-viewer/blob/main/assets/touchosc/comfyui_osc_control.tosc) file into `TouchOSC`. 36 | 5. **Run the Control Panel**: 37 | - In the `TouchOSC` app, open the `comfyui_osc_control` panel. 38 | - Go to the `Gamepad` tab. 39 | 6. **Test Your Setup**: Try using your gamepad to control the buttons in `TouchOSC`. If it works, you’re all set! 40 | 41 | ### Run Workflow in ComfyUI 42 | 43 | 1. **Load Workflow** 44 | - In ComfyUI, load the file [example_osc_control_002_live_portrait_with_gamepad.json](https://github.com/VrchStudio/comfyui-web-viewer/blob/main/example_workflows/example_osc_control_002_live_portrait_with_gamepad.json) 45 | 2. **Set Server Address** 46 | - Go to `Server Settings` group panel 47 | - Update `Server Address` to your ComfyUI Server IP address, e.g. `192.168.1.100` 48 | 3. **Select Portrait Image** 49 | - You could use [sample_pic_01_woman_head.png](https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/refs/heads/main/assets/images/sample_pic_01_woman_head.png) as an example portait to control 50 | 4. **Enable Auto Queue** 51 | - Enable and select `Extra options` -> `Auto Queue` -> `instant` 52 | 5. **Run Workflow** 53 | - Press `Queue Prompt` button to start executing the workflow 54 | - Click `[Open Web Viewer]` button to view the portrait in a separate window 55 | 6. **Use Your Gamepad** 56 | - Grab your gamepad and enjoy controlling the portrait with it! 57 | 58 | ### Cheat Code 59 | 60 | ``` 61 | Head Move (pitch/yaw) --- Left Stick 62 | Head Move (rotate/roll) - Left Stick + A 63 | Pupil Move -------------- Right Stick 64 | Smile ------------------- Left Trigger + Right Bumper 65 | Wink -------------------- Left Trigger + Y 66 | Blink ------------------- Right Trigger + Left Bumper 67 | Eyebrow ----------------- Left Trigger + X 68 | Oral - aaa -------------- Right Trigger + Pad Left 69 | Oral - eee -------------- Right Trigger + Pad Up 70 | Oral - woo -------------- Right Trigger + Pad Right 71 | ``` 72 | 73 | ### Advanced Tips 74 | 75 | 1. If you are an expert on OSC communcation protocol, you may change / update / add more OSC controls by using the `OSC Control nodes` in `ComfyUI Web Viewer` ComfyUI custome node. 76 | 77 | 78 | ## Materials 79 | 80 | - ComfyUI workflow: [example_osc_control_002_live_portrait_with_gamepad.json](https://github.com/VrchStudio/comfyui-web-viewer/blob/main/example_workflows/example_osc_control_002_live_portrait_with_gamepad.json) 81 | - TouchOSC panel file: [comfyui_osc_control.tosc](https://github.com/VrchStudio/comfyui-web-viewer/blob/main/assets/touchosc/comfyui_osc_control.tosc) 82 | - Sample portrait picture: [sample_pic_01_woman_head.png](https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/refs/heads/main/assets/images/sample_pic_01_woman_head.png) -------------------------------------------------------------------------------- /docs/tutorial_002_fast_image_to_video_by_ltx_video_and_ollama.md: -------------------------------------------------------------------------------- 1 | 2 | ## TL;DR 3 | 4 | This ComfyUI workflow leverages the powerful **LTX Videos + STG Framework** to create high-quality, motion-rich animations effortlessly. Here’s what it offers: 5 | 6 | 1. **Fast and Efficient Motion Picture Generation**: 7 | Transform a static image into a 3-6 seconds motion picture in just 15-30 seconds using a local GPU, ensuring both speed and quality. 8 | 2. **Advanced Autocaption and Video Prompt Generator**: 9 | Combines the capabilities of **Florence2** and **Llama3.2** as Image-to-Video-Prompt assistants, enabled via custom ComfyUI nodes. Simply upload an image, and the workflow generates a stunning motion picture based on it. 10 | 3. **Also Support User's Customised Instruction**: 11 | Includes an optional **User Input** node, allowing you to add specific instructions to further tailor the generated content, adjusting the style, theme, or narrative to match your vision. 12 | 13 | This workflow provides a streamlined and customizable solution for generating AI-driven motion pictures with minimal effort. 14 | 15 | - You can [download the workflow from here](https://github.com/VrchStudio/comfyui-web-viewer/blob/main/example_workflows/example_web_viewer_003_video_web_viewer.json) 16 | 17 | ![](../example_workflows/example_web_viewer_003_video_web_viewer.png) 18 | 19 | ## Preparations 20 | 21 | ### Download Tools and Models 22 | - **Ollama - Llama3.2**: 23 | - [https://ollama.com/library/llama3.2](https://ollama.com/library/llama3.2) 24 | - **Florence-2-Large-FT**: 25 | - Auto-downloaded on first use 26 | - **LTX-Video-2B v0.9**: 27 | - Location: `ComfyUI/models/checkpoints` 28 | - [https://huggingface.co/Lightricks/LTX-Video/blob/main/ltx-video-2b-v0.9.safetensors](https://huggingface.co/Lightricks/LTX-Video/blob/main/ltx-video-2b-v0.9.safetensors) 29 | - **T5XXL_FP8_E4M3FN**: 30 | - Location: `ComfyUI/models/clip` 31 | - [https://huggingface.co/comfyanonymous/flux_text_encoders/blob/main/t5xxl_fp8_e4m3fn.safetensors](https://huggingface.co/comfyanonymous/flux_text_encoders/blob/main/t5xxl_fp8_e4m3fn.safetensors) 32 | 33 | ### Install ComfyUI Custom Nodes 34 | 35 | **Note**: You could use `ComfyUI Manager` to install them in ComfyUI webpage directly. 36 | 37 | - **ComfyUI Ollama**: 38 | - [https://github.com/stavsap/comfyui-ollama](https://github.com/stavsap/comfyui-ollama) 39 | - **ComfyUI Florence2**: 40 | - [https://github.com/kijai/ComfyUI-Florence2](https://github.com/kijai/ComfyUI-Florence2) 41 | - **ComfyUI LTXVideo**: 42 | - [https://github.com/Lightricks/ComfyUI-LTXVideo](https://github.com/Lightricks/ComfyUI-LTXVideo) 43 | - **ComfyUI LTXTricks**: 44 | - [https://github.com/logtd/ComfyUI-LTXTricks](https://github.com/logtd/ComfyUI-LTXTricks) 45 | - **ComfyUI Web Viewer**: 46 | - [https://github.com/VrchStudio/comfyui-web-viewer](https://github.com/VrchStudio/comfyui-web-viewer) 47 | - **ComfyUI Video Helper Suite**: 48 | - [https://github.com/Kosinkadink/ComfyUI-VideoHelperSuite](https://github.com/Kosinkadink/ComfyUI-VideoHelperSuite) 49 | 50 | ## How to Use 51 | 52 | ### Run Workflow in ComfyUI 53 | 54 | When running this workflow, the following key parameters in the control panel could be adjusted: 55 | 56 | - **Frame Max Size**: 57 | Sets the maximum resolution for generated frames (e.g., 384, 512, 640, 768). 58 | - **Frames**: 59 | Controls the total number of frames in the motion picture (e.g., 49, 65, 97, 121). 60 | - **Steps**: 61 | Specifies the number of iterations per frame; higher steps improve quality but increase processing time. 62 | - **User Input** (Optional): 63 | Allows users to input extra instruction to customize the generated content, directly affecting the output's style and theme. 64 | **Note**: the test shows that the user's input might not always work. 65 | 66 | Use these settings in ComfyUI's Control Panel Group to adjust the workflow for optimal results. 67 | 68 | ### Display Your Generated Artwork Outside of ComfyUI 69 | 70 | The **`VIDEO Web Viewer @ vrch.ai`** node (available via the [`ComfyUI Web Viewer`](https://github.com/VrchStudio/comfyui-web-viewer) plugin) makes it easy to showcase your generated motion pictures. 71 | 72 | Simply click the `[Open Web Viewer]` button in the `Video Post-Process` group panel, and a web page will open to display your motion picture independently. 73 | 74 | For advanced users, this feature even supports simultaneous viewing on multiple devices, giving you greater flexibility and accessibility! :D 75 | 76 | ### Advanced Tips 77 | 78 | You may further tweak Ollama's `System Prompt` to adjust the motion picture's style or quality: 79 | 80 | ``` 81 | You are transforming user inputs into descriptive prompts for generating AI Videos. Follow these steps to produce the final description: 82 | 1. English Only: The entire output must be written in English with 80-150 words. 83 | 2. Concise, Single Paragraph: Begin with a single paragraph that describes the scene, focusing on key actions in sequence. 84 | 3. Detailed Actions and Appearance: Clearly detail the movements of characters, objects, and relevant elements in the scene. Include brief, essential visual details that highlight distinctive features. 85 | 4. Contextual Setting: Provide minimal yet effective background details that establish time, place, and atmosphere. Keep it relevant to the scene without unnecessary elaboration. 86 | 5. Camera Angles and Movements: Mention camera perspectives or movements that shape the viewer’s experience, but keep it succinct. 87 | 6. Lighting and Color: Incorporate lighting conditions and color tones that set the scene’s mood and complement the actions. 88 | 7. Source Type: Reflect the nature of the footage (e.g., real-life, animation) naturally in the description. 89 | 8. No Additional Commentary: Do not include instructions, reasoning steps, or any extra text outside the described scene. Do not provide explanations or justifications—only the final prompt description. 90 | 91 | Example Style: 92 | • A group of colorful hot air balloons take off at dawn in Cappadocia, Turkey. Dozens of balloons in various bright colors and patterns slowly rise into the pink and orange sky. Below them, the unique landscape of Cappadocia unfolds, with its distinctive “fairy chimneys” - tall, cone-shaped rock formations scattered across the valley. The rising sun casts long shadows across the terrain, highlighting the otherworldly topography. 93 | ``` 94 | 95 | ## References 96 | 97 | - ComfyUI Web Viewer GitHub Repo: [ComfyUI Web Viewer](https://github.com/VrchStudio/comfyui-web-viewer) 98 | - ComfyUI Video Web Viewer workflow: [example_web_viewer_003_video_web_viewer.json](https://github.com/VrchStudio/comfyui-web-viewer/blob/main/example_workflows/example_web_viewer_003_video_web_viewer.json) 99 | - LTX Videos GitHub Repo: [LTX Videos](https://github.com/Lightricks/ComfyUI-LTXVideo) 100 | - Another LTX IMAGE to MOTION PICTURE with STG and autocaption workflow: [civitai link](https://civitai.com/models/995093/ltx-image-to-video-with-stg-and-autocaption-workflow) 101 | -------------------------------------------------------------------------------- /docs/tutorial_004_real_time_voice_clone_by_f5_tts.md: -------------------------------------------------------------------------------- 1 | # Tutorial 004: Real Time Voice Clone by F5-TTS 2 | 3 | ![](../example_workflows/example_web_viewer_005_audio_web_viewer_f5_tts.png) 4 | 5 | You can **[Download the Workflow Here](https://github.com/VrchStudio/comfyui-web-viewer/blob/main/example_workflows/example_web_viewer_005_audio_web_viewer_f5_tts.json)** 6 | 7 | ## TL;DR 8 | 9 | - **Effortlessly Clone Your Voice in Real-Time**: Utilize the power of F5-TTS integrated with ComfyUI to create a high-quality voice clone with just a few clicks. 10 | - **Simple Setup**: Install the necessary custom nodes, download the provided workflow, and get started within minutes without any complex configurations. 11 | - **Interactive Voice Recording**: Use the `Audio Recorder @ vrch.ai` node to easily record your voice, which is then automatically processed by the F5-TTS model. 12 | - **Instant Playback**: Listen to your cloned voice immediately through the `Audio Web Viewer @ vrch.ai` node. 13 | - **Versatile Applications**: Perfect for creating personalized voice assistants, dubbing content, or experimenting with AI-driven voice technologies. 14 | 15 | --- 16 | 17 | ## Preparations 18 | 19 | ### Install Main Custom Nodes 20 | 21 | 1. **ComfyUI-F5-TTS** 22 | - Simply search and install **"ComfyUI-F5-TTS"** in ComfyUI Manager. 23 | - See [https://github.com/niknah/ComfyUI-F5-TTS](https://github.com/niknah/ComfyUI-F5-TTS) 24 | 25 | 2. **ComfyUI-Web-Viewer** 26 | - Simply search and install **"ComfyUI Web Viewer"** in ComfyUI Manager. 27 | - See [https://github.com/VrchStudio/comfyui-web-viewer](https://github.com/VrchStudio/comfyui-web-viewer) 28 | 29 | ### Install Other Necessary Custom Nodes 30 | 31 | - **ComfyUI Chibi Nodes** 32 | - Simply search and install **"ComfyUI-Chibi-Nodes"** in ComfyUI Manager. 33 | - see [https://github.com/chibiace/ComfyUI-Chibi-Nodes](https://github.com/chibiace/ComfyUI-Chibi-Nodes) 34 | 35 | --- 36 | 37 | ## How to Use 38 | 39 | ### 1. Run Workflow in ComfyUI 40 | 41 | 1. **Open the Workflow** 42 | - Import the [example_web_viewer_005_audio_web_viewer_f5_tts](https://github.com/VrchStudio/comfyui-web-viewer/blob/main/example_workflows/example_web_viewer_005_audio_web_viewer_f5_tts.json) workflow into ComfyUI. 43 | 44 | 2. **Record Your Voice** 45 | - In the **`Audio Recorder @ vrch.ai`** node: 46 | - Press and hold the **[Press and Hold to Record]** button. 47 | - Read aloud the text in `Sample Text to Record` (for example): 48 | > This is a test recording to make AI clone my voice. 49 | - Your recorded voice will be automatically sent to the **`F5-TTS`** node for processing. 50 | 51 | 3. **Trigger the TTS** 52 | - If the process doesn’t start automatically, click the **[Queue]** button in the **`F5-TTS`** node. 53 | - Enter custom text in the `Text To Read` field, such as: 54 | > I've seen things you people wouldn't believe. Attack ships on fire off the shoulder of Orion. I've watched c-beams glitter in the dark near the Tannhauser Gate. 55 | > All those ... 56 | > moments will be lost in time, 57 | > like tears ... in rain. 58 | 59 | 4. **Listen to Your Cloned Voice** 60 | - The text in the **`Text To Read`** node will be read aloud by the AI using your cloned voice. 61 | 62 | 5. **Enjoy the Result!** 63 | - Experiment with different phrases or voices to see how well the model clones your tone and style. 64 | 65 | ### 2. Use Your Cloned Voice Outside of ComfyUI 66 | 67 | The **`Audio Web Viewer @ vrch.ai`** node from the [**ComfyUI Web Viewer**](https://github.com/VrchStudio/comfyui-web-viewer) plugin makes it simple to showcase your cloned voice or share it with others. 68 | 69 | 1. **Open the Audio Web Viewer page**: 70 | - In the **`Audio Web Viewer @ vrch.ai`** node, click the **[Open Web Viewer]** button. 71 | - A new browser window (or tab) will open, playing your cloned voice. 72 | 73 | 2. **Accessing Saved Audio**: 74 | - The `.mp3` file is stored in your ComfyUI `output` folder, within the `web_viewer` subfolder (e.g., `web_viewer/channel_1.mp3`). 75 | - Share this file or open the generated URL from any device on your network (if your server is accessible externally). 76 | 77 | > **Tips:** Make sure your **Server** address and **SSL** settings in `Audio Web Viewer` are correct for your network environment. If you want to access the audio from another device or over the internet, ensure that the server IP/domain is reachable and ports are open. 78 | 79 | --- 80 | 81 | ## References 82 | 83 | - **Real Time Voice Clone Workflow**: 84 | [example_web_viewer_005_audio_web_viewer_f5_tts](https://github.com/VrchStudio/comfyui-web-viewer/blob/main/example_workflows/example_web_viewer_005_audio_web_viewer_f5_tts.json) 85 | - **ComfyUI Web Viewer GitHub Repo**: 86 | [https://github.com/VrchStudio/comfyui-web-viewer](https://github.com/VrchStudio/comfyui-web-viewer) 87 | - **ComfyUI F5 TTS GitHub Repo**: 88 | [https://github.com/niknah/ComfyUI-F5-TTS](https://github.com/niknah/ComfyUI-F5-TTS) 89 | - **F5-TTS GitHub Repo**: 90 | [https://github.com/SWivid/F5-TTS/](https://github.com/SWivid/F5-TTS/) -------------------------------------------------------------------------------- /docs/tutorial_005_storytelling_with_text_srt_player.md: -------------------------------------------------------------------------------- 1 | # Tutorial 005: Storytelling with Text SRT Player 2 | 3 | ![](../example_workflows/example_text_nodes_001_text_srt_player.png) 4 | 5 | ## TL;DR 6 | 7 | This tutorial guides you on how to use the **TEXT SRT Playe @ vrch.ai** node from [**ComfyUI Web Viewer**](https://github.com/VrchStudio/comfyui-web-viewer), leveraging SRT subtitle files with timestamps to dynamically control storytelling images generation. Combined with the **IMAGE Preview in Background** node, you can create a fully automated, timestamp-based storytelling experience with real-time AI-generated visuals in ComfyUI. 8 | 9 | **Practical Use Cases**: 10 | - Interactive storytelling for exhibitions or live performances. 11 | - Dynamic AI-generated visual experiences for streaming or live events. 12 | - Real-time creative sessions with precise narrative control. 13 | 14 | ## Preparations 15 | 16 | ### Install Main Custom Nodes 17 | 18 | 1. **ComfyUI-Web-Viewer** 19 | - Simply search and install **"ComfyUI Web Viewer"** in ComfyUI Manager. 20 | - See [https://github.com/VrchStudio/comfyui-web-viewer](https://github.com/VrchStudio/comfyui-web-viewer) 21 | 22 | 23 | ### Install Other Necessary Custom Nodes 24 | 25 | - **ComfyUI Chibi Nodes** 26 | - Simply search and install **"ComfyUI-Chibi-Nodes"** in ComfyUI Manager. 27 | - see [https://github.com/chibiace/ComfyUI-Chibi-Nodes](https://github.com/chibiace/ComfyUI-Chibi-Nodes) 28 | 29 | ## How to Use 30 | 31 | ### 1. Run Workflow in ComfyUI 32 | 33 | 1. **Open the Workflow** 34 | - Import the [example_text_nodes_001_text_srt_player](https://github.com/VrchStudio/comfyui-web-viewer/blob/main/example_workflows/example_text_nodes_001_text_srt_player.json) workflow into ComfyUI. 35 | 36 | 2. **Use SRT Format Text for Storytelling Images Generation** 37 | - Here is an example: 38 | ``` 39 | 1 40 | 00:00:00,000 --> 00:00:05,000 41 | A cozy medieval village waking up under a gentle golden sunrise; friendly cottages and blooming flowers surrounded by soft, magical mist; a cheerful young adventurer receives a mysterious, sparkling letter delivered by a small bluebird; storybook fantasy illustration, soft colors, warm lighting, highly detailed, 8K resolution. 42 | 43 | 2 44 | 00:00:05,000 --> 00:00:10,000 45 | A lively village marketplace filled with smiling villagers trading colorful fruits, toys, and treats; the adventurer eagerly studying a playful treasure map decorated with stars and symbols; magical butterflies flutter gently around; storybook fantasy illustration, soft colors, morning sunlight, highly detailed, 8K resolution. 46 | 47 | 3 48 | 00:00:10,000 --> 00:00:15,000 49 | A lush, enchanted forest glowing with magical green lights; giant, friendly trees with smiling faces, moss-covered paths; the young adventurer exploring curiously, cute woodland creatures peeking playfully through the leaves; storybook fantasy illustration, gentle atmosphere, soft colors, highly detailed, 8K resolution. 50 | 51 | 4 52 | 00:00:15,000 --> 00:00:20,000 53 | A secret clearing in the magical forest; a kind old wizard in robes decorated with stars kindly offering friendly advice, surrounded by softly glowing rune stones arranged in a circle; sparkling magic dust floating gently; storybook fantasy illustration, warm sunlight rays, whimsical details, highly detailed, 8K resolution. 54 | 55 | 5 56 | 00:00:20,000 --> 00:00:25,000 57 | A magical desert filled with gentle, rolling golden dunes under a bright cheerful sun; the adventurer happily riding a friendly camel through playful winds swirling magical sparkles in the air; storybook fantasy illustration, soft colors, joyful mood, highly detailed, 8K resolution. 58 | 59 | 6 60 | 00:00:25,000 --> 00:00:30,000 61 | Ancient ruins gently peeking from the sands; friendly-looking giant statues engraved with playful symbols; the young adventurer excitedly discovering a hidden clue while friendly desert foxes watch with curiosity; storybook fantasy illustration, whimsical atmosphere, soft lighting, highly detailed, 8K resolution. 62 | 63 | 7 64 | 00:00:30,000 --> 00:00:35,000 65 | Inside a hidden magical temple softly lit by twinkling torches; walls decorated with glowing playful glyphs; the adventurer cheerfully tiptoeing through the halls, enchanted echoes whispering gently; storybook fantasy illustration, cozy atmosphere, detailed textures, soft lighting, highly detailed, 8K resolution. 66 | 67 | 8 68 | 00:00:35,000 --> 00:00:40,000 69 | A magical chamber gently shrouded in sparkling mist; a giant, friendly stone guardian with softly glowing eyes greets the adventurer who bravely holds up a wooden sword; playful curiosity and awe; storybook fantasy illustration, gentle shadows, whimsical atmosphere, highly detailed, 8K resolution. 70 | 71 | 9 72 | 00:00:40,000 --> 00:00:45,000 73 | A beautiful magical altar glowing with gentle, colorful lights; the adventurer joyfully holds up a brightly glowing crystal treasure surrounded by swirling sparkles; happy magical creatures celebrating around; storybook fantasy illustration, dreamy atmosphere, soft colors, highly detailed, 8K resolution. 74 | 75 | 10 76 | 00:00:45,000 --> 00:00:50,000 77 | The adventurer happily returns to the welcoming village at sunset; villagers cheerfully celebrate with lanterns, music, and treats; gentle golden sunset casting warm lights; joyful mood, storybook fantasy illustration, cozy atmosphere, soft colors, highly detailed, 8K resolution. 78 | ``` 79 | - Edit and past the SRT Format text into `TEXT SRT Player @ vrch.ai` node 80 | 81 | 3. **Enable and Start Queue (Instant) Mode** 82 | - Activate **Queue (Instant)** mode in ComfyUI for real-time prompt processing. 83 | 4. **Click [Play SRT File] Button to Start SRT Text Switch** 84 | - Click the **[Play SRT File]** button in the `TEXT SRT Player @ vrch.ai` node to initiate playback. 85 | - The node automatically sends prompts to the workflow according to timestamps. 86 | 87 | 5. **Enjoy Real-time Visual Storytelling!** 88 | - Generated images appear dynamically in your ComfyUI interface via the **`IMAGE Preview in Background @ vrch.ai`** node. 89 | - Images will switch automatically based on SRT timestamps, delivering a smooth storytelling experience. 90 | 91 | 92 | ### 2. Using Your Artworks Outside of ComfyUI 93 | 94 | To showcase your real-time AI-generated visuals externally: 95 | 96 | - Replace the node **`IMAGE Preview in Background @ vrch.ai`** with the **`IMAGE Web Viewer @ vrch.ai`** node. 97 | - After replacement, click the **[Open Web Viewer]** button. 98 | - A new browser window/tab will open, displaying your real-time storytelling images. 99 | 100 | #### Tips for External Viewing: 101 | 1. Make sure your **Server** address and **SSL** settings in `IMAGE Web Viewer @ vrch.ai` are correct for your network environment. If you want to access the audio from another device or over the internet, ensure that the server IP/domain is reachable and ports are open. 102 | 2. If you’re encountering a CORS policy error with a message like this: 103 | > `WARNING: request with non matching host and origin 127.0.0.1 != vrch.ai, returning 403` 104 | 105 | you can resolve this by launching the ComfyUI service with the `--enable-cors-header` flag appended, e.g. 106 | `python main.py --enable-cors-header` 107 | 108 | 109 | ## References 110 | 111 | - **Storytelling with Text SRT Player Workflow**: 112 | [example_text_nodes_001_text_srt_player](https://github.com/VrchStudio/comfyui-web-viewer/blob/main/example_workflows/example_text_nodes_001_text_srt_player.json) 113 | - **ComfyUI Web Viewer GitHub Repo**: 114 | [https://github.com/VrchStudio/comfyui-web-viewer](https://github.com/VrchStudio/comfyui-web-viewer) 115 | 116 | --- 117 | 118 | Enjoy your journey into dynamic storytelling with ComfyUI! -------------------------------------------------------------------------------- /docs/tutorial_006_srt_to_audio_picture_book.md: -------------------------------------------------------------------------------- 1 | # Tutorial 006: Audio Picture Book with Your Own Voice 2 | 3 | ![](../example_workflows/example_others_004_srt_to_audio_picture_book.png) 4 | 5 | You can **[Download the Workflow Hewre](https://github.com/VrchStudio/comfyui-web-viewer/blob/main/example_workflows/example_others_004_srt_to_audio_picture_book.json)** 6 | 7 | ## TL;DR 8 | 9 | This tutorial guides you on how to create an **AI-powered Audio Picture Book using your own cloned voice** with the [**ComfyUI Web Viewer**](https://github.com/VrchStudio/comfyui-web-viewer). It utilizes the **Audio Recorder**, **TEXT SRT Player**, and web viewer nodes to transform timed SRT subtitle files into synchronized audio-visual storytelling experiences. Your voice recordings are cloned to narrate stories, while AI dynamically generates matching visuals in real-time. 10 | 11 | **Practical Use Cases**: 12 | - Personalized audio books with visually rich storytelling. 13 | - Real-time, interactive visual and audio content for educational or entertainment settings. 14 | - Immersive presentations and performances with custom voice narration. 15 | 16 | **🚀 Support Us:** 17 | 18 | If you find the **ComfyUI Web Viewer** useful or inspiring, consider supporting us: 19 | 20 | - 💖 **Sponsor**: Help us maintain and enhance the project through [GitHub Sponsors](https://github.com/sponsors/VrchStudio). 21 | - ⭐ **Star the Project**: A star on GitHub greatly motivates us and helps increase visibility! 22 | - 📩 **Business Inquiries**: For commercial collaborations, reach us at [hi@vrch.io](mailto:hi@vrch.io). 23 | 24 | --- 25 | 26 | ## Preparations 27 | 28 | ### Download Tools and Models 29 | - **Ollama - Llama3.2**: 30 | - [https://ollama.com/library/llama3.2](https://ollama.com/library/llama3.2) 31 | - **T5XXL_FP8_E4M3FN**: 32 | - Location: `ComfyUI/models/clip` 33 | - [https://huggingface.co/comfyanonymous/flux_text_encoders/blob/main/t5xxl_fp8_e4m3fn.safetensors](https://huggingface.co/comfyanonymous/flux_text_encoders/blob/main/t5xxl_fp8_e4m3fn.safetensors) 34 | - **Flux Turbo-Alpha lora**: 35 | - Location: `ComfyUI/models/loras` 36 | - See [https://huggingface.co/alimama-creative/FLUX.1-Turbo-Alpha/tree/main](https://huggingface.co/alimama-creative/FLUX.1-Turbo-Alpha/tree/main) 37 | - **Note**: please rename the model filename to be `FLUX.1-Turbo-Alpha.safetensors` 38 | 39 | ### Install Main Custom Nodes 40 | 41 | 1. **ComfyUI-F5-TTS** 42 | - Simply search and install **"ComfyUI-F5-TTS"** in ComfyUI Manager. 43 | - See [https://github.com/niknah/ComfyUI-F5-TTS](https://github.com/niknah/ComfyUI-F5-TTS) 44 | 45 | 2. **ComfyUI-Web-Viewer** 46 | - Simply search and install **"ComfyUI Web Viewer"** in ComfyUI Manager. 47 | - See [https://github.com/VrchStudio/comfyui-web-viewer](https://github.com/VrchStudio/comfyui-web-viewer) 48 | 49 | 3. **ComfyUI Ollama**: 50 | - Simply search and install **"comfyui-ollama"** in ComfyUI Manager. 51 | - See [https://github.com/stavsap/comfyui-ollama](https://github.com/stavsap/comfyui-ollama) 52 | 53 | 4. **ComfyUI TeaCache**: 54 | - Simply search and install **"ComfyUI-TeaCache"** in ComfyUI Manasger. 55 | - See [https://github.com/welltop-cn/ComfyUI-TeaCache](https://github.com/welltop-cn/ComfyUI-TeaCache) 56 | 57 | ### Install Other Necessary Custom Nodes 58 | 59 | - **ComfyUI Chibi Nodes** 60 | - Simply search and install **"ComfyUI-Chibi-Nodes"** in ComfyUI Manager. 61 | - See [https://github.com/chibiace/ComfyUI-Chibi-Nodes](https://github.com/chibiace/ComfyUI-Chibi-Nodes) 62 | - **RGThree's ComfyUI Nodes**: 63 | - Simply search and install **"rgthree-comfy"** in ComfyUI Manager. 64 | - [https://github.com/rgthree/rgthree-comfy](https://github.com/rgthree/rgthree-comfy) 65 | 66 | ## How to Use 67 | 68 | ### Run Workflow in ComfyUI 69 | 70 | 1. **Open the Workflow** 71 | - Import the [example_others_004_srt_to_audio_picture_book](https://github.com/VrchStudio/comfyui-web-viewer/blob/main/example_workflows/example_others_004_srt_to_audio_picture_book.json) workflow into ComfyUI. 72 | 2. **Record Your Voice** 73 | - In the **`Audio Recorder @ vrch.ai`** node: 74 | - Press and hold the **[Press and Hold to Record]** button. 75 | - Read aloud the text in `Sample Text to Record` (for example): 76 | > This is a test recording to make AI clone my voice. 77 | 3. **Trigger the SRT Player** 78 | - Change the **`[Queue]`** button to **`[Queue (Instant)]`** 79 | - In the **`TEXT SRT Player @vrch.ai`** node: 80 | - Click **`[Play SRT File]`** button to start SRT player 81 | - Click **`[Queue (Instant)]`** button to start Infinite Queue 82 | 4. **Open Audio Web Viewer Page for Audio Play** 83 | - In the **`AUDIO Web Viewer @ vrch.ai`** node, click the **[Open Web Viewer]** button. 84 | - A new browser window (or tab) will open, playing the story audio with your cloned voice. 85 | 5. **Open Image Instant Viewer Page for Image Display** 86 | - In the **`IMAGE Web Viewer @ vrch.ai`** node, click the **[Open Web Viewer]** button. 87 | - A new browser window (or tab) will open, display the story pictures generated. 88 | 6. **(Optinal) Enable Preview Image in Background for Image Preview in ComfyUI** 89 | - In the **`IMAGE Preview in Background @ vrch.ai`** node, enable `background_display` option 90 | - The story pictures will be displayed in ComfyUI web page as background 91 | 92 | ### Example: SRT Format Stories 93 | 94 | #### Story One 95 | 96 | ``` 97 | 1 98 | 00:00:00,000 --> 00:00:13,000 99 | Little Deer opened her eyes as moonlight gently caressed the forest. 100 | The woods at night were wrapped in a silvery veil, peaceful and enchanting. 101 | 102 | 2 103 | 00:00:13,000 --> 00:00:25,000 104 | “Little deer, a star has lost its way,” whispered the owl from the tall oak tree, 105 | his eyes glowing softly in the moonlight. 106 | 107 | 3 108 | 00:00:25,000 --> 00:00:42,000 109 | Tiptoeing gently through the forest, Little Deer passed a sleeping hedgehog curled beneath leaves, 110 | and a little fox smiling sweetly in his dreams. 111 | 112 | 4 113 | 00:00:42,000 --> 00:00:56,000 114 | Soon, little deer spotted a star gently floating on the lake, 115 | glimmering quietly and rocking with the waves. 116 | 117 | 4 118 | 00:00:56,000 --> 00:01:10,000 119 | Little deer carefully waded into the water and whispered softly, 120 | “Don’t be afraid, little star. I'll help you find your way back home.” 121 | 122 | 6 123 | 00:01:10,000 --> 00:01:25,000 124 | She looked upward, where countless stars twinkled brightly in the velvet sky, 125 | each gently waving, waiting for their lost friend to return. 126 | 127 | 7 128 | 00:01:25,000 --> 00:01:42,000 129 | Gently lifting the star back into the sky, little deer watched as it shone brighter, 130 | joining friends that twinkled happily in thanks. 131 | 132 | 8 133 | 00:01:42,000 --> 00:02:00,000 134 | Little deer lay down softly beneath the tree, closed her eyes, 135 | and drifted into sweet dreams, as the forest sparkled brighter than ever, 136 | wrapping every animal in the gentlest sleep. 137 | ``` 138 | 139 | #### Story Two 140 | 141 | ``` 142 | 1 143 | 00:00:00,000 --> 00:00:13,000 144 | As little rabbit opened her eyes, the moonlight softly touched the forest. 145 | The night was quiet and calm, like a gentle lullaby. 146 | 147 | 2 148 | 00:00:13,000 --> 00:00:25,000 149 | “Little rabbit, the forest is yours tonight,” 150 | said a tiny firefly, glowing gently like a star. 151 | 152 | 3 153 | 00:00:25,000 --> 00:00:42,000 154 | She hopped through the woods, gently checking on her sleeping friends— 155 | the hedgehog curled up tight, the little fox smiling sweetly. 156 | 157 | 4 158 | 00:00:42,000 --> 00:00:56,000 159 | Suddenly, rabbit saw the reflection of the moon in the pond, 160 | but the little moon in the water was crying softly. 161 | 162 | 5 163 | 00:00:56,000 --> 00:01:10,000 164 | “Don’t cry, little moon, I’m here,” 165 | rabbit said, crafting a leaf boat and gently sailing toward the center. 166 | 167 | 6 168 | 00:01:10,000 --> 00:01:25,000 169 | She gently rocked the moon to sleep, 170 | until the little reflection smiled again, shimmering happily. 171 | 172 | 7 173 | 00:01:25,000 --> 00:01:42,000 174 | Back on shore, rabbit looked up to the sky, 175 | where the real moon smiled warmly down at them. 176 | 177 | 8 178 | 00:01:42,000 --> 00:02:00,000 179 | Rabbit closed her eyes, cuddling softly beneath the trees. 180 | Tonight, every animal slept peacefully under the gentle moonlight. 181 | ``` 182 | 183 | 184 | ## References 185 | 186 | - **Audio Picture Book Workflow**: 187 | [example_others_004_srt_to_audio_picture_book](https://github.com/VrchStudio/comfyui-web-viewer/blob/main/example_workflows/example_others_004_srt_to_audio_picture_book.json) 188 | - **ComfyUI Web Viewer GitHub Repo**: 189 | [https://github.com/VrchStudio/comfyui-web-viewer](https://github.com/VrchStudio/comfyui-web-viewer) 190 | 191 | 192 | 193 | -------------------------------------------------------------------------------- /docs/tutorial_007_live_portrait_with_native_gamepad.md: -------------------------------------------------------------------------------- 1 | # Tutorial 007: Unleash Real-Time Avatar Control with Your Native Gamepad! 2 | 3 | ## TL;DR 4 | 5 | Ready for some serious fun? 🚀 This guide shows how to integrate *native* gamepad support directly into ComfyUI in real time using the `ComfyUI Web Viewer` custom nodes, unlocking a new world of interactive possibilities! 🎮 6 | 7 | * **Native Gamepad Support:** Use `ComfyUI Web Viewer` nodes (`Gamepad Loader @ vrch.ai`, `Xbox Controller Mapper @ vrch.ai`) to connect your gamepad directly via the browser's API – no external apps needed. 8 | * **Interactive Control:** Control live portraits, animations, or *any* workflow parameter in real-time using your favorite controller's joysticks and buttons. 9 | * **Enhanced Playfulness:** Make your ComfyUI workflows more dynamic and fun by adding direct, physical input for controlling expressions, movements, and more. 10 | 11 | ![](../example_workflows/example_gamepad_nodes_002_live_portrait.png) 12 | 13 | ## Preparations 14 | 15 | 1. **Install `ComfyUI Web Viewer` custom node**: 16 | * Method 1: Search for `ComfyUI Web Viewer` in ComfyUI Manager. 17 | * Method 2: Install from GitHub: [https://github.com/VrchStudio/comfyui-web-viewer](https://github.com/VrchStudio/comfyui-web-viewer) 18 | 2. **Install `Advanced Live Portrait` custom node**: 19 | * Method 1: Search for `ComfyUI-AdvancedLivePortrait` in ComfyUI Manager. 20 | * Method 2: Install from GitHub: [https://github.com/PowerHouseMan/ComfyUI-AdvancedLivePortrait](https://github.com/PowerHouseMan/ComfyUI-AdvancedLivePortrait) 21 | 3. **Download `Workflow Example: Live Portrait + Native Gamepad` workflow**: 22 | * Download it from here: [example_gamepad_nodes_002_live_portrait.json](https://github.com/VrchStudio/comfyui-web-viewer/blob/main/example_workflows/example_gamepad_nodes_002_live_portrait.json) 23 | 4. **Connect Your Gamepad**: 24 | * Connect a compatible gamepad (e.g., Xbox controller) to your computer via USB or Bluetooth. Ensure your browser recognizes it. Most modern browsers (Chrome, Edge) have good Gamepad API support. 25 | 26 | ## How to Play 27 | 28 | ### Run Workflow in ComfyUI 29 | 30 | 1. **Load Workflow**: 31 | * In ComfyUI, load the file [example_gamepad_nodes_002_live_portrait.json](https://github.com/VrchStudio/comfyui-web-viewer/blob/main/example_workflows/example_gamepad_nodes_002_live_portrait.json). 32 | 2. **Check Gamepad Connection**: 33 | * Locate the `Gamepad Loader @ vrch.ai` node in the workflow. 34 | * Ensure your gamepad is detected. The `name` field should show your gamepad's identifier. If not, try pressing some buttons on the gamepad. You might need to adjust the `index` if you have multiple controllers connected. 35 | 3. **Select Portrait Image**: 36 | * Locate the `Load Image` node (or similar) feeding into the `Advanced Live Portrait` setup. 37 | * You could use [sample_pic_01_woman_head.png](https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/refs/heads/main/assets/images/sample_pic_01_woman_head.png) as an example portrait to control. 38 | 4. **Enable Auto Queue**: 39 | * Enable `Extra options` -> `Auto Queue`. Set it to `instant` or a suitable mode for real-time updates. 40 | 5. **Run Workflow**: 41 | * Press the `Queue Prompt` button to start executing the workflow. 42 | * Optionally, use a `Web Viewer` node (like `VrchImageWebSocketWebViewerNode` included in the example) and click its `[Open Web Viewer]` button to view the portrait in a separate, cleaner window. 43 | 6. **Use Your Gamepad**: 44 | * Grab your gamepad and enjoy controlling the portrait with it! 45 | 46 | ### Cheat Code (Based on Example Workflow) 47 | 48 | ``` 49 | Head Move (pitch/yaw) --- Left Stick 50 | Head Move (rotate/roll) - Left Stick + A 51 | Pupil Move -------------- Right Stick 52 | Smile ------------------- Left Trigger + Right Bumper 53 | Wink -------------------- Left Trigger + Y 54 | Blink ------------------- Right Trigger + Left Bumper 55 | Eyebrow ----------------- Left Trigger + X 56 | Oral - aaa -------------- Right Trigger + Pad Left 57 | Oral - eee -------------- Right Trigger + Pad Up 58 | Oral - woo -------------- Right Trigger + Pad Right 59 | ``` 60 | 61 | *Note: This mapping is defined within the example workflow using logic nodes (`Float Remap`, `Boolean Logic`, etc.) connected to the outputs of the `Xbox Controller Mapper @ vrch.ai` node. You can customize these connections to change the controls.* 62 | 63 | ### Advanced Tips 64 | 65 | 1. You can modify the connections between the `Xbox Controller Mapper @ vrch.ai` node and the `Advanced Live Portrait` inputs (via remap/logic nodes) to customize the control scheme entirely. 66 | 2. Explore the different outputs of the `Gamepad Loader @ vrch.ai` and `Xbox Controller Mapper @ vrch.ai` nodes to access various button states (boolean, integer, float) and stick/trigger values. See the [Gamepad Nodes Documentation](./gamepad_nodes.md) for details. 67 | 68 | ## Materials 69 | 70 | - ComfyUI workflow: [example_gamepad_nodes_002_live_portrait.json](https://github.com/VrchStudio/comfyui-web-viewer/blob/main/example_workflows/example_gamepad_nodes_002_live_portrait.json) 71 | - Sample portrait picture: [sample_pic_01_woman_head.png](https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/refs/heads/main/assets/images/sample_pic_01_woman_head.png) 72 | -------------------------------------------------------------------------------- /docs/websocket_nodes.md: -------------------------------------------------------------------------------- 1 | ### Node: `IMAGE WebSocket Web Viewer @ vrch.ai` (vrch.ai/viewer) 2 | 3 | 1. **Add the `IMAGE WebSocket Web Viewer @ vrch.ai` node to your ComfyUI workflow.** 4 | 5 | 2. **Configure the Node:** 6 | - **Image Input:** 7 | - **`images`**: Connect the image(s) you wish to display in the WebSocket-based web viewer. 8 | - **Channel:** 9 | - **`channel`**: Select a channel number from **"1"** to **"8"** (default is **"1"**) to differentiate WebSocket connections. 10 | - **Server:** 11 | - **`server`**: Enter the server's domain or IP address along with its port in the format `IP:PORT`. The default typically uses your IP and port **8001** (e.g., **`127.0.0.1:8001`**). 12 | - **Image Format:** 13 | - **`format`**: Choose the image format for transmission, supporting **PNG** and **JPEG** (default is **JPEG**). 14 | - **Websocket Parameters:** 15 | - **`number_of_images`**: Set the number of images to load (default is **`4`**, range: 1-99). 16 | - **`image_display_duration`**: Duration to display each image in milliseconds (default is **`1000`**, range: 1-10000). 17 | - **`fade_anim_duration`**: Duration of fade animation in milliseconds (default is **`200`**, range: 1-10000). 18 | - **`mixBlendMode`**: Set the blend mode for image transitions (default is **`none`**). Options include **`normal`**, **`multiply`**, **`screen`**, etc. 19 | - **`enableLoop`**: Toggle whether to loop playback of images (default is **`True`**). 20 | - **`enableUpdateOnEnd`**: Toggle whether to update the image cache only at the end of playback (default is **`False`**). 21 | - **Server Messages:** Save and send server messages to its web page viewer. 22 | - **Save Settings:** Toggle whether to save the websocket settings to a JSON file. When enabled, sends settings via WebSocket. 23 | - **Window Dimensions:** 24 | - **`window_width`**: Set the width of the web viewer window (default is **1280**). 25 | - **`window_height`**: Set the height of the web viewer window (default is **960**). 26 | - **Show URL:** 27 | - **`show_url`**: Toggle the display of the constructed URL in the interface. When enabled, the **`url`** field becomes visible. 28 | - **Debug Mode:** 29 | - **`debug`**: Enable this option to print detailed debug information to the console for troubleshooting. 30 | - **Extra Parameters:** 31 | - **`extra_params`**: The extra parameters for Image WebSocket Web Viewer, see [websocket_nodes_extra_params.md](./websocket_nodes_extra_params.md) 32 | - **URL Input:** 33 | - **`url`**: This field is automatically updated with a constructed URL based on your inputs (server, channel, extra parameters, etc.). You can control its visibility with the **`show_url`** option. 34 | 35 | 3. **Open Web Viewer:** 36 | - Click the **"Open Web Viewer"** button to launch the generated URL in a new browser window, where your image will be displayed in real time via the WebSocket connection. 37 | 38 | **Notes:** 39 | - Make sure that the server address and configuration are correct and that the server is accessible. 40 | - This node uses the WebSocket protocol to transmit image data in real time to the specified channel; ensure your client browser supports WebSocket connections. 41 | - When **`save_settings`** is enabled, a JSON with your websocket parameters is sent via the same WebSocket connection. 42 | - When debug mode is enabled, the node outputs detailed logs to the console, which can help you track the image transmission process and troubleshoot any issues. 43 | 44 | --- 45 | 46 | ### Node: `WebSocket Server @ vrch.ai` (vrch.ai/viewer/websocket) 47 | 48 | 1. **Add the `WebSocket Server @ vrch.ai` node to your ComfyUI workflow.** 49 | 50 | 2. **Configure the Node:** 51 | - **Server:** 52 | - **`server`**: Enter the server's address and port in the format `IP:PORT`. The default is typically your local IP address and port **8001** (e.g., **`127.0.0.1:8001`**). 53 | - **Debug Mode:** 54 | - **`debug`**: Enable this option to print detailed debug information to the console for troubleshooting. 55 | 56 | 3. **Server Status Indicator:** 57 | - The node displays a status indicator that shows whether the server is running: 58 | - **Grey "Stopped"**: The WebSocket server is not running 59 | - **Green "Running"**: The WebSocket server is active and accepting connections 60 | 61 | 4. **Usage:** 62 | - This node must be executed in your workflow to start the WebSocket server 63 | - Once running, it handles communication for all WebSocket nodes (Image and JSON) 64 | - The server maintains separate connection paths for different data types (/image and /json) 65 | - Multiple clients can connect simultaneously to the same server 66 | 67 | **Notes:** 68 | - This node is required for any WebSocket-based communication in your workflow. 69 | - Only one server can run on a specific IP:port combination. 70 | - If a server is already running on the specified address and port, the node will use the existing server. 71 | - WebSocket connections are maintained even when your workflow is not actively running. 72 | - When debug mode is enabled, the server outputs detailed connection logs to the console. 73 | 74 | --- 75 | 76 | ### Node: `IMAGE WebSocket Channel Loader @ vrch.ai` (vrch.ai/viewer/websocket) 77 | 78 | 1. **Add the `IMAGE WebSocket Channel Loader @ vrch.ai` node to your ComfyUI workflow.** 79 | 80 | 2. **Configure the Node:** 81 | - **`channel`**: Select a channel number from **"1"** to **"8"** (default is **"1"**) to specify which WebSocket channel to listen on. 82 | - **`server`**: Enter the server's domain or IP address along with its port in the format `IP:PORT`. The default typically uses your IP and port **8001** (e.g., **`127.0.0.1:8001`**). 83 | - **`placeholder`**: Choose the placeholder to display when no image data is received. Options: 84 | - **"black"**: pure black placeholder image. 85 | - **"white"**: pure white placeholder image. 86 | - **"grey"**: mid-grey placeholder image. 87 | - **"image"**: use the provided **`default_image`** as placeholder. Requires supplying **`default_image`**. The node detects changes to this image and outputs it immediately once per change. 88 | - **`default_image`**: *(Optional)* Image to use when **`placeholder`** is set to **"image"**. 89 | - **`debug`**: Enable this option to print detailed debug information to the console for troubleshooting. 90 | 91 | 3. **Outputs:** 92 | - **`IMAGE`**: The output image tensor or placeholder. 93 | - **`IS_DEFAULT_IMAGE`**: Boolean flag, `True` if the `IMAGE` output is the provided `default_image`, `False` otherwise. 94 | 95 | 4. **Receiving Images:** 96 | - This node automatically connects to the specified WebSocket channel and listens for incoming image data. 97 | - When an image is received, it will be processed and made available as the `IMAGE` output with `IS_DEFAULT_IMAGE` set to `False`. 98 | - If **`placeholder`** is set to **"image"** and a new **`default_image`** was provided since the last execution, it is output immediately once (`IMAGE` + `True`). 99 | - If no WebSocket image is received afterward, the **`default_image`** is used as the placeholder output (`IS_DEFAULT_IMAGE=False` in that case?). 100 | 101 | **Notes:** 102 | - This node is designed to work with the `IMAGE WebSocket Web Viewer @ vrch.ai` node, receiving the images it broadcasts. 103 | - The node automatically establishes and maintains WebSocket connections, reconnecting if the connection is lost. 104 | - The node continuously monitors for new images, allowing your workflow to react to images sent from any source that connects to the same WebSocket channel. 105 | - When debug mode is enabled, the node outputs detailed logs to the console, which can help you track the image reception process and troubleshoot any issues. 106 | 107 | --- 108 | 109 | ### Node: `JSON WebSocket Sender @ vrch.ai` (vrch.ai/viewer/websocket) 110 | 111 | 1. **Add the `JSON WebSocket Sender @ vrch.ai` node to your ComfyUI workflow.** 112 | 113 | 2. **Configure the Node:** 114 | - **JSON Input:** 115 | - **`json_string`**: Enter the JSON string you want to send over WebSocket. This should be a properly formatted JSON string. 116 | - **Channel:** 117 | - **`channel`**: Select a channel number from **"1"** to **"8"** (default is **"1"**) to differentiate WebSocket connections. 118 | - **Server:** 119 | - **`server`**: Enter the server's domain or IP address along with its port in the format `IP:PORT`. The default typically uses your IP and port **8001** (e.g., **`127.0.0.1:8001`**). 120 | - **Debug Mode:** 121 | - **`debug`**: Enable this option to print detailed debug information to the console for troubleshooting. 122 | 123 | 3. **Sending JSON Data:** 124 | - The node validates the JSON string before sending. If the string is not valid JSON, the node will raise a ValueError. 125 | - Once validated, the JSON data is sent to the specified WebSocket channel and path (/json). 126 | - The validated JSON is also available as an output that can be connected to other nodes. 127 | 128 | **Notes:** 129 | - This node works with the `WebSocket Server @ vrch.ai` node, which must be running to establish connections. 130 | - Ensure your JSON string is properly formatted to avoid validation errors. 131 | - The JSON data is sent through the WebSocket as a raw string. 132 | - When debug mode is enabled, the node outputs detailed logs to the console, including the content of the sent JSON data. 133 | 134 | --- 135 | 136 | ### Node: `JSON WebSocket Channel Loader @ vrch.ai` (vrch.ai/viewer/websocket) 137 | 138 | 1. **Add the `JSON WebSocket Channel Loader @ vrch.ai` node to your ComfyUI workflow.** 139 | 140 | 2. **Configure the Node:** 141 | - **Channel:** 142 | - **`channel`**: Select a channel number from **"1"** to **"8"** (default is **"1"**) to specify which WebSocket channel to listen on. 143 | - **Server:** 144 | - **`server`**: Enter the server's domain or IP address along with its port in the format `IP:PORT`. The default typically uses your IP and port **8001** (e.g., **`127.0.0.1:8001`**). 145 | - **Debug Mode:** 146 | - **`debug`**: Enable this option to print detailed debug information to the console for troubleshooting. 147 | - **Default JSON:** 148 | - **`default_json_string`**: (Optional) Enter a default JSON string to use when no data is received. This should be a properly formatted JSON string. 149 | 150 | 3. **Receiving JSON Data:** 151 | - This node automatically connects to the specified WebSocket channel and listens for incoming JSON data. 152 | - When JSON data is received, it will be parsed and made available as an output that can be connected to other nodes in your workflow. 153 | - If no JSON data has been received yet, the default JSON string will be used (if provided), otherwise an empty object `{}` is returned. 154 | - If the provided default JSON string is not valid JSON, the node will raise a ValueError. 155 | 156 | **Notes:** 157 | - This node is designed to work with the `JSON WebSocket Sender @ vrch.ai` node, receiving the JSON data it broadcasts. 158 | - The node automatically establishes and maintains WebSocket connections, reconnecting if the connection is lost. 159 | - The node continuously monitors for new JSON data, allowing your workflow to react to data sent from any source that connects to the same WebSocket channel. 160 | - When debug mode is enabled, the node outputs detailed logs to the console, which can help you track the JSON data reception process. 161 | 162 | -------------------------------------------------------------------------------- /docs/websocket_nodes_extra_params.md: -------------------------------------------------------------------------------- 1 | ### Image WebSocket Viewer 2 | 3 | | Name | Code | Description | Type | Value | Remarks | 4 | |-------------------------|---------------------|--------------------------------------------------------------------|---------|---------|---------------------------------------------------------------------------------------| 5 | | Auto Fullscreen | autoFullscreen | Automatically enters fullscreen mode on start. | Boolean | false | When true, the viewer enters fullscreen automatically. | 6 | | Skip Settings Page | skipSettingsPage | Bypasses the settings page to start the viewer immediately. | Boolean | false | Directly initiates the viewer if true. | 7 | | Image Display Option | imageDisplayOption | Determines how the image is scaled or fitted. | String | fit | Options: original, fit, resize, crop. | 8 | | Image Alignment | imageAlignment | Sets the alignment of the image within its container. | String | center | Options: top-left, top, top-right, left, center, right, bottom-left, bottom, bottom-right. | 9 | | Background Colour | bgColourPicker | Sets the background color of the image container. | String | #222222 | Accepts hex color codes. | 10 | | Image Display Duration | imageDisplayDuration| Duration (ms) for which each image is displayed. | Number | 1000 | Determines how long each image remains visible. | 11 | | Fade Animation Duration | fadeAnimDuration | Duration (ms) of the fade transition between images. | Number | 200 | Controls the speed of the fade effect. | 12 | | Number Of Images | numberOfImages | Total number of images in the image cache sequence. | Number | 4 | Specifies how many images are cached and cycled through. | 13 | | Load Remote Settings | loadRemoteSettings | Load Remote Settings from the server side. | Boolean | false | When true, the viewer loads settings from remote server. | 14 | | Show Server Messages | showServerMessages | Display messages sent from the server side. | Boolean | false | When true, the viewer displays server messages on the screen. | 15 | | Blend Mode | mixBlendMode | Sets the blend mode for image transitions. | String | none | Options: none, normal, multiply, screen, overlay, darken, lighten, etc. | 16 | | Loop Playback | enableLoop | Toggles whether to loop playback of images. | Boolean | true | When true, the playback loops indefinitely. | 17 | | Update on End | enableUpdateOnEnd | Updates the image cache only at the end of playback. | Boolean | false | When true, the cache is updated after the last image is displayed. | 18 | -------------------------------------------------------------------------------- /example_workflows/example_audio_nodes_001_audio_recorder.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/example_workflows/example_audio_nodes_001_audio_recorder.jpg -------------------------------------------------------------------------------- /example_workflows/example_audio_nodes_001_audio_recorder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/example_workflows/example_audio_nodes_001_audio_recorder.png -------------------------------------------------------------------------------- /example_workflows/example_gamepad_nodes_001_basic.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/example_workflows/example_gamepad_nodes_001_basic.jpeg -------------------------------------------------------------------------------- /example_workflows/example_gamepad_nodes_001_basic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/example_workflows/example_gamepad_nodes_001_basic.jpg -------------------------------------------------------------------------------- /example_workflows/example_gamepad_nodes_001_basic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/example_workflows/example_gamepad_nodes_001_basic.png -------------------------------------------------------------------------------- /example_workflows/example_gamepad_nodes_002_live_portrait.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/example_workflows/example_gamepad_nodes_002_live_portrait.jpeg -------------------------------------------------------------------------------- /example_workflows/example_gamepad_nodes_002_live_portrait.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/example_workflows/example_gamepad_nodes_002_live_portrait.png -------------------------------------------------------------------------------- /example_workflows/example_image_nodes_001_preview_in_background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/example_workflows/example_image_nodes_001_preview_in_background.jpg -------------------------------------------------------------------------------- /example_workflows/example_image_nodes_001_preview_in_background.json: -------------------------------------------------------------------------------- 1 | { 2 | "last_node_id": 11, 3 | "last_link_id": 10, 4 | "nodes": [ 5 | { 6 | "id": 7, 7 | "type": "CLIPTextEncode", 8 | "pos": [ 9 | 413, 10 | 389 11 | ], 12 | "size": [ 13 | 425.27801513671875, 14 | 180.6060791015625 15 | ], 16 | "flags": {}, 17 | "order": 3, 18 | "mode": 0, 19 | "inputs": [ 20 | { 21 | "name": "clip", 22 | "type": "CLIP", 23 | "link": 5 24 | } 25 | ], 26 | "outputs": [ 27 | { 28 | "name": "CONDITIONING", 29 | "type": "CONDITIONING", 30 | "links": [ 31 | 6 32 | ], 33 | "slot_index": 0 34 | } 35 | ], 36 | "properties": { 37 | "Node name for S&R": "CLIPTextEncode" 38 | }, 39 | "widgets_values": [ 40 | "text, watermark" 41 | ] 42 | }, 43 | { 44 | "id": 6, 45 | "type": "CLIPTextEncode", 46 | "pos": [ 47 | 415, 48 | 186 49 | ], 50 | "size": [ 51 | 422.84503173828125, 52 | 164.31304931640625 53 | ], 54 | "flags": {}, 55 | "order": 2, 56 | "mode": 0, 57 | "inputs": [ 58 | { 59 | "name": "clip", 60 | "type": "CLIP", 61 | "link": 3 62 | } 63 | ], 64 | "outputs": [ 65 | { 66 | "name": "CONDITIONING", 67 | "type": "CONDITIONING", 68 | "links": [ 69 | 4 70 | ], 71 | "slot_index": 0 72 | } 73 | ], 74 | "properties": { 75 | "Node name for S&R": "CLIPTextEncode" 76 | }, 77 | "widgets_values": [ 78 | "beautiful scenery nature glass bottle landscape, , purple galaxy bottle," 79 | ] 80 | }, 81 | { 82 | "id": 5, 83 | "type": "EmptyLatentImage", 84 | "pos": [ 85 | 473, 86 | 609 87 | ], 88 | "size": [ 89 | 315, 90 | 106 91 | ], 92 | "flags": {}, 93 | "order": 0, 94 | "mode": 0, 95 | "inputs": [], 96 | "outputs": [ 97 | { 98 | "name": "LATENT", 99 | "type": "LATENT", 100 | "links": [ 101 | 2 102 | ], 103 | "slot_index": 0 104 | } 105 | ], 106 | "properties": { 107 | "Node name for S&R": "EmptyLatentImage" 108 | }, 109 | "widgets_values": [ 110 | 512, 111 | 512, 112 | 1 113 | ] 114 | }, 115 | { 116 | "id": 8, 117 | "type": "VAEDecode", 118 | "pos": [ 119 | 1209, 120 | 188 121 | ], 122 | "size": [ 123 | 210, 124 | 46 125 | ], 126 | "flags": {}, 127 | "order": 5, 128 | "mode": 0, 129 | "inputs": [ 130 | { 131 | "name": "samples", 132 | "type": "LATENT", 133 | "link": 7 134 | }, 135 | { 136 | "name": "vae", 137 | "type": "VAE", 138 | "link": 8 139 | } 140 | ], 141 | "outputs": [ 142 | { 143 | "name": "IMAGE", 144 | "type": "IMAGE", 145 | "links": [ 146 | 10 147 | ], 148 | "slot_index": 0 149 | } 150 | ], 151 | "properties": { 152 | "Node name for S&R": "VAEDecode" 153 | }, 154 | "widgets_values": [] 155 | }, 156 | { 157 | "id": 4, 158 | "type": "CheckpointLoaderSimple", 159 | "pos": [ 160 | 26, 161 | 474 162 | ], 163 | "size": [ 164 | 315, 165 | 98 166 | ], 167 | "flags": {}, 168 | "order": 1, 169 | "mode": 0, 170 | "inputs": [], 171 | "outputs": [ 172 | { 173 | "name": "MODEL", 174 | "type": "MODEL", 175 | "links": [ 176 | 1 177 | ], 178 | "slot_index": 0 179 | }, 180 | { 181 | "name": "CLIP", 182 | "type": "CLIP", 183 | "links": [ 184 | 3, 185 | 5 186 | ], 187 | "slot_index": 1 188 | }, 189 | { 190 | "name": "VAE", 191 | "type": "VAE", 192 | "links": [ 193 | 8 194 | ], 195 | "slot_index": 2 196 | } 197 | ], 198 | "properties": { 199 | "Node name for S&R": "CheckpointLoaderSimple" 200 | }, 201 | "widgets_values": [ 202 | "sdxl/dreamshaperXL_v21TurboDPMSDE.safetensors" 203 | ] 204 | }, 205 | { 206 | "id": 3, 207 | "type": "KSampler", 208 | "pos": [ 209 | 863, 210 | 186 211 | ], 212 | "size": [ 213 | 315, 214 | 262 215 | ], 216 | "flags": {}, 217 | "order": 4, 218 | "mode": 0, 219 | "inputs": [ 220 | { 221 | "name": "model", 222 | "type": "MODEL", 223 | "link": 1 224 | }, 225 | { 226 | "name": "positive", 227 | "type": "CONDITIONING", 228 | "link": 4 229 | }, 230 | { 231 | "name": "negative", 232 | "type": "CONDITIONING", 233 | "link": 6 234 | }, 235 | { 236 | "name": "latent_image", 237 | "type": "LATENT", 238 | "link": 2 239 | } 240 | ], 241 | "outputs": [ 242 | { 243 | "name": "LATENT", 244 | "type": "LATENT", 245 | "links": [ 246 | 7 247 | ], 248 | "slot_index": 0 249 | } 250 | ], 251 | "properties": { 252 | "Node name for S&R": "KSampler" 253 | }, 254 | "widgets_values": [ 255 | 476477157969202, 256 | "randomize", 257 | 6, 258 | 2, 259 | "euler", 260 | "normal", 261 | 1 262 | ] 263 | }, 264 | { 265 | "id": 11, 266 | "type": "VrchImagePreviewBackgroundNode", 267 | "pos": [ 268 | 1210.3291015625, 269 | 343.22265625 270 | ], 271 | "size": [ 272 | 310.79998779296875, 273 | 137.5263214111328 274 | ], 275 | "flags": {}, 276 | "order": 6, 277 | "mode": 0, 278 | "inputs": [ 279 | { 280 | "name": "image", 281 | "type": "IMAGE", 282 | "link": 10 283 | } 284 | ], 285 | "outputs": [], 286 | "properties": { 287 | "Node name for S&R": "VrchImagePreviewBackgroundNode" 288 | }, 289 | "widgets_values": [ 290 | "1", 291 | true, 292 | 300, 293 | "fit" 294 | ] 295 | } 296 | ], 297 | "links": [ 298 | [ 299 | 1, 300 | 4, 301 | 0, 302 | 3, 303 | 0, 304 | "MODEL" 305 | ], 306 | [ 307 | 2, 308 | 5, 309 | 0, 310 | 3, 311 | 3, 312 | "LATENT" 313 | ], 314 | [ 315 | 3, 316 | 4, 317 | 1, 318 | 6, 319 | 0, 320 | "CLIP" 321 | ], 322 | [ 323 | 4, 324 | 6, 325 | 0, 326 | 3, 327 | 1, 328 | "CONDITIONING" 329 | ], 330 | [ 331 | 5, 332 | 4, 333 | 1, 334 | 7, 335 | 0, 336 | "CLIP" 337 | ], 338 | [ 339 | 6, 340 | 7, 341 | 0, 342 | 3, 343 | 2, 344 | "CONDITIONING" 345 | ], 346 | [ 347 | 7, 348 | 3, 349 | 0, 350 | 8, 351 | 0, 352 | "LATENT" 353 | ], 354 | [ 355 | 8, 356 | 4, 357 | 2, 358 | 8, 359 | 1, 360 | "VAE" 361 | ], 362 | [ 363 | 10, 364 | 8, 365 | 0, 366 | 11, 367 | 0, 368 | "IMAGE" 369 | ] 370 | ], 371 | "groups": [], 372 | "config": {}, 373 | "extra": { 374 | "ds": { 375 | "scale": 0.7972024500000371, 376 | "offset": [ 377 | -166.02319149998993, 378 | 292.0220343359259 379 | ] 380 | }, 381 | "node_versions": { 382 | "comfy-core": "0.3.15", 383 | "comfyui-web-viewer": "8f590e6bd02b41ef73be1d26f9fa0c8014e899dd" 384 | } 385 | }, 386 | "version": 0.4 387 | } -------------------------------------------------------------------------------- /example_workflows/example_image_nodes_001_preview_in_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/example_workflows/example_image_nodes_001_preview_in_background.png -------------------------------------------------------------------------------- /example_workflows/example_key_control_001_basic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/example_workflows/example_key_control_001_basic.jpg -------------------------------------------------------------------------------- /example_workflows/example_key_control_001_basic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/example_workflows/example_key_control_001_basic.png -------------------------------------------------------------------------------- /example_workflows/example_osc_control_001_basic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/example_workflows/example_osc_control_001_basic.jpg -------------------------------------------------------------------------------- /example_workflows/example_osc_control_001_basic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/example_workflows/example_osc_control_001_basic.png -------------------------------------------------------------------------------- /example_workflows/example_osc_control_002_live_portrait_with_gamepad.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/example_workflows/example_osc_control_002_live_portrait_with_gamepad.jpg -------------------------------------------------------------------------------- /example_workflows/example_osc_control_002_live_portrait_with_gamepad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/example_workflows/example_osc_control_002_live_portrait_with_gamepad.png -------------------------------------------------------------------------------- /example_workflows/example_osc_control_003_ic-light.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/example_workflows/example_osc_control_003_ic-light.jpg -------------------------------------------------------------------------------- /example_workflows/example_osc_control_003_ic-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/example_workflows/example_osc_control_003_ic-light.png -------------------------------------------------------------------------------- /example_workflows/example_osc_control_004_ic-light_and_live_portrait.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/example_workflows/example_osc_control_004_ic-light_and_live_portrait.jpg -------------------------------------------------------------------------------- /example_workflows/example_osc_control_004_ic-light_and_live_portrait.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/example_workflows/example_osc_control_004_ic-light_and_live_portrait.png -------------------------------------------------------------------------------- /example_workflows/example_others_001_text_to_image_8k.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/example_workflows/example_others_001_text_to_image_8k.jpg -------------------------------------------------------------------------------- /example_workflows/example_others_001_text_to_image_8k.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/example_workflows/example_others_001_text_to_image_8k.png -------------------------------------------------------------------------------- /example_workflows/example_others_002_autorun_instant_queue.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/example_workflows/example_others_002_autorun_instant_queue.jpg -------------------------------------------------------------------------------- /example_workflows/example_others_002_autorun_instant_queue.json: -------------------------------------------------------------------------------- 1 | { 2 | "last_node_id": 12, 3 | "last_link_id": 9, 4 | "nodes": [ 5 | { 6 | "id": 7, 7 | "type": "CLIPTextEncode", 8 | "pos": { 9 | "0": 413, 10 | "1": 389 11 | }, 12 | "size": { 13 | "0": 425.27801513671875, 14 | "1": 180.6060791015625 15 | }, 16 | "flags": {}, 17 | "order": 4, 18 | "mode": 0, 19 | "inputs": [ 20 | { 21 | "name": "clip", 22 | "type": "CLIP", 23 | "link": 5 24 | } 25 | ], 26 | "outputs": [ 27 | { 28 | "name": "CONDITIONING", 29 | "type": "CONDITIONING", 30 | "links": [ 31 | 6 32 | ], 33 | "slot_index": 0 34 | } 35 | ], 36 | "properties": { 37 | "Node name for S&R": "CLIPTextEncode" 38 | }, 39 | "widgets_values": [ 40 | "text, watermark" 41 | ] 42 | }, 43 | { 44 | "id": 6, 45 | "type": "CLIPTextEncode", 46 | "pos": { 47 | "0": 415, 48 | "1": 186 49 | }, 50 | "size": { 51 | "0": 422.84503173828125, 52 | "1": 164.31304931640625 53 | }, 54 | "flags": {}, 55 | "order": 3, 56 | "mode": 0, 57 | "inputs": [ 58 | { 59 | "name": "clip", 60 | "type": "CLIP", 61 | "link": 3 62 | } 63 | ], 64 | "outputs": [ 65 | { 66 | "name": "CONDITIONING", 67 | "type": "CONDITIONING", 68 | "links": [ 69 | 4 70 | ], 71 | "slot_index": 0 72 | } 73 | ], 74 | "properties": { 75 | "Node name for S&R": "CLIPTextEncode" 76 | }, 77 | "widgets_values": [ 78 | "beautiful scenery nature glass bottle landscape, , purple galaxy bottle," 79 | ] 80 | }, 81 | { 82 | "id": 5, 83 | "type": "EmptyLatentImage", 84 | "pos": { 85 | "0": 473, 86 | "1": 609 87 | }, 88 | "size": { 89 | "0": 315, 90 | "1": 106 91 | }, 92 | "flags": {}, 93 | "order": 0, 94 | "mode": 0, 95 | "inputs": [], 96 | "outputs": [ 97 | { 98 | "name": "LATENT", 99 | "type": "LATENT", 100 | "links": [ 101 | 2 102 | ], 103 | "slot_index": 0 104 | } 105 | ], 106 | "properties": { 107 | "Node name for S&R": "EmptyLatentImage" 108 | }, 109 | "widgets_values": [ 110 | 512, 111 | 512, 112 | 1 113 | ] 114 | }, 115 | { 116 | "id": 8, 117 | "type": "VAEDecode", 118 | "pos": { 119 | "0": 1209, 120 | "1": 188 121 | }, 122 | "size": { 123 | "0": 210, 124 | "1": 46 125 | }, 126 | "flags": {}, 127 | "order": 6, 128 | "mode": 0, 129 | "inputs": [ 130 | { 131 | "name": "samples", 132 | "type": "LATENT", 133 | "link": 7 134 | }, 135 | { 136 | "name": "vae", 137 | "type": "VAE", 138 | "link": 8 139 | } 140 | ], 141 | "outputs": [ 142 | { 143 | "name": "IMAGE", 144 | "type": "IMAGE", 145 | "links": [ 146 | 9 147 | ], 148 | "slot_index": 0 149 | } 150 | ], 151 | "properties": { 152 | "Node name for S&R": "VAEDecode" 153 | }, 154 | "widgets_values": [] 155 | }, 156 | { 157 | "id": 9, 158 | "type": "SaveImage", 159 | "pos": { 160 | "0": 1451, 161 | "1": 189 162 | }, 163 | "size": { 164 | "0": 210, 165 | "1": 270 166 | }, 167 | "flags": {}, 168 | "order": 7, 169 | "mode": 0, 170 | "inputs": [ 171 | { 172 | "name": "images", 173 | "type": "IMAGE", 174 | "link": 9 175 | } 176 | ], 177 | "outputs": [], 178 | "properties": { 179 | "Node name for S&R": "SaveImage" 180 | }, 181 | "widgets_values": [ 182 | "ComfyUI" 183 | ] 184 | }, 185 | { 186 | "id": 4, 187 | "type": "CheckpointLoaderSimple", 188 | "pos": { 189 | "0": 26, 190 | "1": 474 191 | }, 192 | "size": { 193 | "0": 315, 194 | "1": 98 195 | }, 196 | "flags": {}, 197 | "order": 1, 198 | "mode": 0, 199 | "inputs": [], 200 | "outputs": [ 201 | { 202 | "name": "MODEL", 203 | "type": "MODEL", 204 | "links": [ 205 | 1 206 | ], 207 | "slot_index": 0 208 | }, 209 | { 210 | "name": "CLIP", 211 | "type": "CLIP", 212 | "links": [ 213 | 3, 214 | 5 215 | ], 216 | "slot_index": 1 217 | }, 218 | { 219 | "name": "VAE", 220 | "type": "VAE", 221 | "links": [ 222 | 8 223 | ], 224 | "slot_index": 2 225 | } 226 | ], 227 | "properties": { 228 | "Node name for S&R": "CheckpointLoaderSimple" 229 | }, 230 | "widgets_values": [ 231 | "sdxl/dreamshaperXL_v21TurboDPMSDE.safetensors" 232 | ] 233 | }, 234 | { 235 | "id": 3, 236 | "type": "KSampler", 237 | "pos": { 238 | "0": 863, 239 | "1": 186 240 | }, 241 | "size": { 242 | "0": 315, 243 | "1": 262 244 | }, 245 | "flags": {}, 246 | "order": 5, 247 | "mode": 0, 248 | "inputs": [ 249 | { 250 | "name": "model", 251 | "type": "MODEL", 252 | "link": 1 253 | }, 254 | { 255 | "name": "positive", 256 | "type": "CONDITIONING", 257 | "link": 4 258 | }, 259 | { 260 | "name": "negative", 261 | "type": "CONDITIONING", 262 | "link": 6 263 | }, 264 | { 265 | "name": "latent_image", 266 | "type": "LATENT", 267 | "link": 2 268 | } 269 | ], 270 | "outputs": [ 271 | { 272 | "name": "LATENT", 273 | "type": "LATENT", 274 | "links": [ 275 | 7 276 | ], 277 | "slot_index": 0 278 | } 279 | ], 280 | "properties": { 281 | "Node name for S&R": "KSampler" 282 | }, 283 | "widgets_values": [ 284 | 54020863898625, 285 | "randomize", 286 | 6, 287 | 2, 288 | "dpmpp_2m_sde", 289 | "karras", 290 | 1 291 | ] 292 | }, 293 | { 294 | "id": 12, 295 | "type": "VrchInstantQueueKeyControlNode", 296 | "pos": { 297 | "0": 891, 298 | "1": 683 299 | }, 300 | "size": { 301 | "0": 441, 302 | "1": 198 303 | }, 304 | "flags": {}, 305 | "order": 2, 306 | "mode": 0, 307 | "inputs": [], 308 | "outputs": [], 309 | "properties": { 310 | "Node name for S&R": "VrchInstantQueueKeyControlNode" 311 | }, 312 | "widgets_values": [ 313 | "instant", 314 | "F2", 315 | true, 316 | 5, 317 | null, 318 | null 319 | ] 320 | } 321 | ], 322 | "links": [ 323 | [ 324 | 1, 325 | 4, 326 | 0, 327 | 3, 328 | 0, 329 | "MODEL" 330 | ], 331 | [ 332 | 2, 333 | 5, 334 | 0, 335 | 3, 336 | 3, 337 | "LATENT" 338 | ], 339 | [ 340 | 3, 341 | 4, 342 | 1, 343 | 6, 344 | 0, 345 | "CLIP" 346 | ], 347 | [ 348 | 4, 349 | 6, 350 | 0, 351 | 3, 352 | 1, 353 | "CONDITIONING" 354 | ], 355 | [ 356 | 5, 357 | 4, 358 | 1, 359 | 7, 360 | 0, 361 | "CLIP" 362 | ], 363 | [ 364 | 6, 365 | 7, 366 | 0, 367 | 3, 368 | 2, 369 | "CONDITIONING" 370 | ], 371 | [ 372 | 7, 373 | 3, 374 | 0, 375 | 8, 376 | 0, 377 | "LATENT" 378 | ], 379 | [ 380 | 8, 381 | 4, 382 | 2, 383 | 8, 384 | 1, 385 | "VAE" 386 | ], 387 | [ 388 | 9, 389 | 8, 390 | 0, 391 | 9, 392 | 0, 393 | "IMAGE" 394 | ] 395 | ], 396 | "groups": [], 397 | "config": {}, 398 | "extra": { 399 | "ds": { 400 | "scale": 1, 401 | "offset": [ 402 | 73.89999999999998, 403 | -86 404 | ] 405 | } 406 | }, 407 | "version": 0.4 408 | } -------------------------------------------------------------------------------- /example_workflows/example_others_002_autorun_instant_queue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/example_workflows/example_others_002_autorun_instant_queue.png -------------------------------------------------------------------------------- /example_workflows/example_others_003_text_to_image_5k_avp_ultra_wide.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/example_workflows/example_others_003_text_to_image_5k_avp_ultra_wide.jpg -------------------------------------------------------------------------------- /example_workflows/example_others_003_text_to_image_5k_avp_ultra_wide.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/example_workflows/example_others_003_text_to_image_5k_avp_ultra_wide.png -------------------------------------------------------------------------------- /example_workflows/example_others_004_srt_to_audio_picture_book.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/example_workflows/example_others_004_srt_to_audio_picture_book.jpg -------------------------------------------------------------------------------- /example_workflows/example_others_004_srt_to_audio_picture_book.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/example_workflows/example_others_004_srt_to_audio_picture_book.png -------------------------------------------------------------------------------- /example_workflows/example_others_005_srt_to_audio_picture_book_sdxl.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/example_workflows/example_others_005_srt_to_audio_picture_book_sdxl.jpg -------------------------------------------------------------------------------- /example_workflows/example_others_005_srt_to_audio_picture_book_sdxl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/example_workflows/example_others_005_srt_to_audio_picture_book_sdxl.png -------------------------------------------------------------------------------- /example_workflows/example_text_nodes_001_text_srt_player.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/example_workflows/example_text_nodes_001_text_srt_player.jpg -------------------------------------------------------------------------------- /example_workflows/example_text_nodes_001_text_srt_player.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/example_workflows/example_text_nodes_001_text_srt_player.png -------------------------------------------------------------------------------- /example_workflows/example_web_viewer_001_image_web_viewer.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/example_workflows/example_web_viewer_001_image_web_viewer.jpg -------------------------------------------------------------------------------- /example_workflows/example_web_viewer_001_image_web_viewer.json: -------------------------------------------------------------------------------- 1 | { 2 | "last_node_id": 23, 3 | "last_link_id": 22, 4 | "nodes": [ 5 | { 6 | "id": 6, 7 | "type": "CLIPTextEncode", 8 | "pos": [ 9 | 415, 10 | 186 11 | ], 12 | "size": [ 13 | 422.84503173828125, 14 | 164.31304931640625 15 | ], 16 | "flags": {}, 17 | "order": 2, 18 | "mode": 0, 19 | "inputs": [ 20 | { 21 | "name": "clip", 22 | "type": "CLIP", 23 | "link": 3 24 | } 25 | ], 26 | "outputs": [ 27 | { 28 | "name": "CONDITIONING", 29 | "type": "CONDITIONING", 30 | "links": [ 31 | 4 32 | ], 33 | "slot_index": 0 34 | } 35 | ], 36 | "properties": { 37 | "Node name for S&R": "CLIPTextEncode" 38 | }, 39 | "widgets_values": [ 40 | "beautiful scenery nature glass bottle landscape, , purple galaxy bottle," 41 | ] 42 | }, 43 | { 44 | "id": 5, 45 | "type": "EmptyLatentImage", 46 | "pos": [ 47 | 473, 48 | 609 49 | ], 50 | "size": [ 51 | 315, 52 | 106 53 | ], 54 | "flags": {}, 55 | "order": 0, 56 | "mode": 0, 57 | "inputs": [], 58 | "outputs": [ 59 | { 60 | "name": "LATENT", 61 | "type": "LATENT", 62 | "links": [ 63 | 2 64 | ], 65 | "slot_index": 0 66 | } 67 | ], 68 | "properties": { 69 | "Node name for S&R": "EmptyLatentImage" 70 | }, 71 | "widgets_values": [ 72 | 512, 73 | 512, 74 | 1 75 | ] 76 | }, 77 | { 78 | "id": 3, 79 | "type": "KSampler", 80 | "pos": [ 81 | 863, 82 | 186 83 | ], 84 | "size": [ 85 | 315, 86 | 262 87 | ], 88 | "flags": {}, 89 | "order": 4, 90 | "mode": 0, 91 | "inputs": [ 92 | { 93 | "name": "model", 94 | "type": "MODEL", 95 | "link": 1 96 | }, 97 | { 98 | "name": "positive", 99 | "type": "CONDITIONING", 100 | "link": 4 101 | }, 102 | { 103 | "name": "negative", 104 | "type": "CONDITIONING", 105 | "link": 6 106 | }, 107 | { 108 | "name": "latent_image", 109 | "type": "LATENT", 110 | "link": 2 111 | } 112 | ], 113 | "outputs": [ 114 | { 115 | "name": "LATENT", 116 | "type": "LATENT", 117 | "links": [ 118 | 7 119 | ], 120 | "slot_index": 0 121 | } 122 | ], 123 | "properties": { 124 | "Node name for S&R": "KSampler" 125 | }, 126 | "widgets_values": [ 127 | 937358264169297, 128 | "randomize", 129 | 20, 130 | 8, 131 | "euler", 132 | "normal", 133 | 1 134 | ] 135 | }, 136 | { 137 | "id": 7, 138 | "type": "CLIPTextEncode", 139 | "pos": [ 140 | 413, 141 | 389 142 | ], 143 | "size": [ 144 | 425.27801513671875, 145 | 180.6060791015625 146 | ], 147 | "flags": {}, 148 | "order": 3, 149 | "mode": 0, 150 | "inputs": [ 151 | { 152 | "name": "clip", 153 | "type": "CLIP", 154 | "link": 5 155 | } 156 | ], 157 | "outputs": [ 158 | { 159 | "name": "CONDITIONING", 160 | "type": "CONDITIONING", 161 | "links": [ 162 | 6 163 | ], 164 | "slot_index": 0 165 | } 166 | ], 167 | "properties": { 168 | "Node name for S&R": "CLIPTextEncode" 169 | }, 170 | "widgets_values": [ 171 | "text, watermark" 172 | ] 173 | }, 174 | { 175 | "id": 4, 176 | "type": "CheckpointLoaderSimple", 177 | "pos": [ 178 | 74, 179 | 267 180 | ], 181 | "size": [ 182 | 315, 183 | 98 184 | ], 185 | "flags": {}, 186 | "order": 1, 187 | "mode": 0, 188 | "inputs": [], 189 | "outputs": [ 190 | { 191 | "name": "MODEL", 192 | "type": "MODEL", 193 | "links": [ 194 | 1 195 | ], 196 | "slot_index": 0 197 | }, 198 | { 199 | "name": "CLIP", 200 | "type": "CLIP", 201 | "links": [ 202 | 3, 203 | 5 204 | ], 205 | "slot_index": 1 206 | }, 207 | { 208 | "name": "VAE", 209 | "type": "VAE", 210 | "links": [ 211 | 8 212 | ], 213 | "slot_index": 2 214 | } 215 | ], 216 | "properties": { 217 | "Node name for S&R": "CheckpointLoaderSimple" 218 | }, 219 | "widgets_values": [ 220 | "sd15/dreamshaper_8.safetensors" 221 | ] 222 | }, 223 | { 224 | "id": 8, 225 | "type": "VAEDecode", 226 | "pos": [ 227 | 1250, 228 | 230 229 | ], 230 | "size": [ 231 | 210, 232 | 46 233 | ], 234 | "flags": {}, 235 | "order": 5, 236 | "mode": 0, 237 | "inputs": [ 238 | { 239 | "name": "samples", 240 | "type": "LATENT", 241 | "link": 7 242 | }, 243 | { 244 | "name": "vae", 245 | "type": "VAE", 246 | "link": 8 247 | } 248 | ], 249 | "outputs": [ 250 | { 251 | "name": "IMAGE", 252 | "type": "IMAGE", 253 | "links": [ 254 | 22 255 | ], 256 | "slot_index": 0 257 | } 258 | ], 259 | "properties": { 260 | "Node name for S&R": "VAEDecode" 261 | }, 262 | "widgets_values": [] 263 | }, 264 | { 265 | "id": 23, 266 | "type": "VrchImageWebViewerNode", 267 | "pos": [ 268 | 1233, 269 | 379 270 | ], 271 | "size": [ 272 | 400, 273 | 386 274 | ], 275 | "flags": {}, 276 | "order": 6, 277 | "mode": 0, 278 | "inputs": [ 279 | { 280 | "name": "images", 281 | "type": "IMAGE", 282 | "link": 22 283 | } 284 | ], 285 | "outputs": [ 286 | { 287 | "name": "IMAGES", 288 | "type": "IMAGE", 289 | "links": null 290 | } 291 | ], 292 | "properties": { 293 | "Node name for S&R": "VrchImageWebViewerNode" 294 | }, 295 | "widgets_values": [ 296 | "1", 297 | "127.0.0.1:8188", 298 | false, 299 | 300, 300 | 150, 301 | false, 302 | "", 303 | 1280, 304 | 960, 305 | false, 306 | "", 307 | "http://vrch.ai/viewer?mode=image&server=127.0.0.1:8188&ssl=false&filename=channel_1.jpeg&path=web_viewer&refreshInterval=300&fadeAnimDuration=150", 308 | "" 309 | ] 310 | } 311 | ], 312 | "links": [ 313 | [ 314 | 1, 315 | 4, 316 | 0, 317 | 3, 318 | 0, 319 | "MODEL" 320 | ], 321 | [ 322 | 2, 323 | 5, 324 | 0, 325 | 3, 326 | 3, 327 | "LATENT" 328 | ], 329 | [ 330 | 3, 331 | 4, 332 | 1, 333 | 6, 334 | 0, 335 | "CLIP" 336 | ], 337 | [ 338 | 4, 339 | 6, 340 | 0, 341 | 3, 342 | 1, 343 | "CONDITIONING" 344 | ], 345 | [ 346 | 5, 347 | 4, 348 | 1, 349 | 7, 350 | 0, 351 | "CLIP" 352 | ], 353 | [ 354 | 6, 355 | 7, 356 | 0, 357 | 3, 358 | 2, 359 | "CONDITIONING" 360 | ], 361 | [ 362 | 7, 363 | 3, 364 | 0, 365 | 8, 366 | 0, 367 | "LATENT" 368 | ], 369 | [ 370 | 8, 371 | 4, 372 | 2, 373 | 8, 374 | 1, 375 | "VAE" 376 | ], 377 | [ 378 | 22, 379 | 8, 380 | 0, 381 | 23, 382 | 0, 383 | "IMAGE" 384 | ] 385 | ], 386 | "groups": [], 387 | "config": {}, 388 | "extra": { 389 | "ds": { 390 | "scale": 0.6830134553650723, 391 | "offset": [ 392 | 78.35213521673094, 393 | 300.57586806701073 394 | ] 395 | }, 396 | "node_versions": { 397 | "comfy-core": "0.3.24", 398 | "comfyui-web-viewer": "4b3c80bd145bf47a7dd44434d57d1688b87907e7" 399 | } 400 | }, 401 | "version": 0.4 402 | } -------------------------------------------------------------------------------- /example_workflows/example_web_viewer_001_image_web_viewer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/example_workflows/example_web_viewer_001_image_web_viewer.png -------------------------------------------------------------------------------- /example_workflows/example_web_viewer_002_image_flipbook_web_viewer.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/example_workflows/example_web_viewer_002_image_flipbook_web_viewer.jpg -------------------------------------------------------------------------------- /example_workflows/example_web_viewer_002_image_flipbook_web_viewer.json: -------------------------------------------------------------------------------- 1 | { 2 | "last_node_id": 13, 3 | "last_link_id": 10, 4 | "nodes": [ 5 | { 6 | "id": 7, 7 | "type": "CLIPTextEncode", 8 | "pos": [ 9 | 413, 10 | 389 11 | ], 12 | "size": [ 13 | 425.27801513671875, 14 | 180.6060791015625 15 | ], 16 | "flags": {}, 17 | "order": 3, 18 | "mode": 0, 19 | "inputs": [ 20 | { 21 | "name": "clip", 22 | "type": "CLIP", 23 | "link": 5 24 | } 25 | ], 26 | "outputs": [ 27 | { 28 | "name": "CONDITIONING", 29 | "type": "CONDITIONING", 30 | "links": [ 31 | 6 32 | ], 33 | "slot_index": 0 34 | } 35 | ], 36 | "properties": { 37 | "Node name for S&R": "CLIPTextEncode" 38 | }, 39 | "widgets_values": [ 40 | "text, watermark" 41 | ] 42 | }, 43 | { 44 | "id": 6, 45 | "type": "CLIPTextEncode", 46 | "pos": [ 47 | 415, 48 | 186 49 | ], 50 | "size": [ 51 | 422.84503173828125, 52 | 164.31304931640625 53 | ], 54 | "flags": {}, 55 | "order": 2, 56 | "mode": 0, 57 | "inputs": [ 58 | { 59 | "name": "clip", 60 | "type": "CLIP", 61 | "link": 3 62 | } 63 | ], 64 | "outputs": [ 65 | { 66 | "name": "CONDITIONING", 67 | "type": "CONDITIONING", 68 | "links": [ 69 | 4 70 | ], 71 | "slot_index": 0 72 | } 73 | ], 74 | "properties": { 75 | "Node name for S&R": "CLIPTextEncode" 76 | }, 77 | "widgets_values": [ 78 | "beautiful scenery nature glass bottle landscape, , purple galaxy bottle," 79 | ] 80 | }, 81 | { 82 | "id": 4, 83 | "type": "CheckpointLoaderSimple", 84 | "pos": [ 85 | 26, 86 | 474 87 | ], 88 | "size": [ 89 | 315, 90 | 98 91 | ], 92 | "flags": {}, 93 | "order": 0, 94 | "mode": 0, 95 | "inputs": [], 96 | "outputs": [ 97 | { 98 | "name": "MODEL", 99 | "type": "MODEL", 100 | "links": [ 101 | 1 102 | ], 103 | "slot_index": 0 104 | }, 105 | { 106 | "name": "CLIP", 107 | "type": "CLIP", 108 | "links": [ 109 | 3, 110 | 5 111 | ], 112 | "slot_index": 1 113 | }, 114 | { 115 | "name": "VAE", 116 | "type": "VAE", 117 | "links": [ 118 | 8 119 | ], 120 | "slot_index": 2 121 | } 122 | ], 123 | "properties": { 124 | "Node name for S&R": "CheckpointLoaderSimple" 125 | }, 126 | "widgets_values": [ 127 | "sdxl/dreamshaperXL_v21TurboDPMSDE.safetensors" 128 | ] 129 | }, 130 | { 131 | "id": 3, 132 | "type": "KSampler", 133 | "pos": [ 134 | 863, 135 | 186 136 | ], 137 | "size": [ 138 | 315, 139 | 262 140 | ], 141 | "flags": {}, 142 | "order": 4, 143 | "mode": 0, 144 | "inputs": [ 145 | { 146 | "name": "model", 147 | "type": "MODEL", 148 | "link": 1 149 | }, 150 | { 151 | "name": "positive", 152 | "type": "CONDITIONING", 153 | "link": 4 154 | }, 155 | { 156 | "name": "negative", 157 | "type": "CONDITIONING", 158 | "link": 6 159 | }, 160 | { 161 | "name": "latent_image", 162 | "type": "LATENT", 163 | "link": 2 164 | } 165 | ], 166 | "outputs": [ 167 | { 168 | "name": "LATENT", 169 | "type": "LATENT", 170 | "links": [ 171 | 7 172 | ], 173 | "slot_index": 0 174 | } 175 | ], 176 | "properties": { 177 | "Node name for S&R": "KSampler" 178 | }, 179 | "widgets_values": [ 180 | 890120932016009, 181 | "randomize", 182 | 6, 183 | 2, 184 | "dpmpp_2m_sde", 185 | "karras", 186 | 1 187 | ] 188 | }, 189 | { 190 | "id": 8, 191 | "type": "VAEDecode", 192 | "pos": [ 193 | 1209, 194 | 188 195 | ], 196 | "size": [ 197 | 210, 198 | 46 199 | ], 200 | "flags": {}, 201 | "order": 5, 202 | "mode": 0, 203 | "inputs": [ 204 | { 205 | "name": "samples", 206 | "type": "LATENT", 207 | "link": 7 208 | }, 209 | { 210 | "name": "vae", 211 | "type": "VAE", 212 | "link": 8 213 | } 214 | ], 215 | "outputs": [ 216 | { 217 | "name": "IMAGE", 218 | "type": "IMAGE", 219 | "links": [ 220 | 10 221 | ], 222 | "slot_index": 0 223 | } 224 | ], 225 | "properties": { 226 | "Node name for S&R": "VAEDecode" 227 | }, 228 | "widgets_values": [] 229 | }, 230 | { 231 | "id": 5, 232 | "type": "EmptyLatentImage", 233 | "pos": [ 234 | 473, 235 | 609 236 | ], 237 | "size": [ 238 | 315, 239 | 106 240 | ], 241 | "flags": {}, 242 | "order": 1, 243 | "mode": 0, 244 | "inputs": [], 245 | "outputs": [ 246 | { 247 | "name": "LATENT", 248 | "type": "LATENT", 249 | "links": [ 250 | 2 251 | ], 252 | "slot_index": 0 253 | } 254 | ], 255 | "properties": { 256 | "Node name for S&R": "EmptyLatentImage" 257 | }, 258 | "widgets_values": [ 259 | 512, 260 | 512, 261 | 4 262 | ] 263 | }, 264 | { 265 | "id": 13, 266 | "type": "VrchImageFlipBookWebViewerNode", 267 | "pos": [ 268 | 1211, 269 | 412 270 | ], 271 | "size": [ 272 | 428.3102111816406, 273 | 513.2980346679688 274 | ], 275 | "flags": {}, 276 | "order": 6, 277 | "mode": 0, 278 | "inputs": [ 279 | { 280 | "name": "images", 281 | "type": "IMAGE", 282 | "link": 10 283 | } 284 | ], 285 | "outputs": [ 286 | { 287 | "name": "IMAGES", 288 | "type": "IMAGE", 289 | "links": null 290 | } 291 | ], 292 | "properties": { 293 | "Node name for S&R": "VrchImageFlipBookWebViewerNode" 294 | }, 295 | "widgets_values": [ 296 | "1", 297 | "127.0.0.1:8188", 298 | false, 299 | 4, 300 | 1000, 301 | 1000, 302 | 300, 303 | "", 304 | false, 305 | 1280, 306 | 960, 307 | true, 308 | "", 309 | "http://vrch.ai/viewer?mode=flipbook&server=127.0.0.1:8188&ssl=false&filename=channel_1.jpeg&path=web_viewer&numberOfImages=4&refreshInterval=1000&imageDisplayDuration=1000&fadeAnimDuration=300", 310 | "" 311 | ] 312 | } 313 | ], 314 | "links": [ 315 | [ 316 | 1, 317 | 4, 318 | 0, 319 | 3, 320 | 0, 321 | "MODEL" 322 | ], 323 | [ 324 | 2, 325 | 5, 326 | 0, 327 | 3, 328 | 3, 329 | "LATENT" 330 | ], 331 | [ 332 | 3, 333 | 4, 334 | 1, 335 | 6, 336 | 0, 337 | "CLIP" 338 | ], 339 | [ 340 | 4, 341 | 6, 342 | 0, 343 | 3, 344 | 1, 345 | "CONDITIONING" 346 | ], 347 | [ 348 | 5, 349 | 4, 350 | 1, 351 | 7, 352 | 0, 353 | "CLIP" 354 | ], 355 | [ 356 | 6, 357 | 7, 358 | 0, 359 | 3, 360 | 2, 361 | "CONDITIONING" 362 | ], 363 | [ 364 | 7, 365 | 3, 366 | 0, 367 | 8, 368 | 0, 369 | "LATENT" 370 | ], 371 | [ 372 | 8, 373 | 4, 374 | 2, 375 | 8, 376 | 1, 377 | "VAE" 378 | ], 379 | [ 380 | 10, 381 | 8, 382 | 0, 383 | 13, 384 | 0, 385 | "IMAGE" 386 | ] 387 | ], 388 | "groups": [], 389 | "config": {}, 390 | "extra": { 391 | "ds": { 392 | "scale": 0.6830134553650723, 393 | "offset": [ 394 | 76.38120591985603, 395 | 287.10573212951056 396 | ] 397 | }, 398 | "node_versions": { 399 | "comfy-core": "0.3.24", 400 | "comfyui-web-viewer": "4b3c80bd145bf47a7dd44434d57d1688b87907e7" 401 | } 402 | }, 403 | "version": 0.4 404 | } -------------------------------------------------------------------------------- /example_workflows/example_web_viewer_002_image_flipbook_web_viewer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/example_workflows/example_web_viewer_002_image_flipbook_web_viewer.png -------------------------------------------------------------------------------- /example_workflows/example_web_viewer_003_video_web_viewer.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/example_workflows/example_web_viewer_003_video_web_viewer.jpg -------------------------------------------------------------------------------- /example_workflows/example_web_viewer_003_video_web_viewer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/example_workflows/example_web_viewer_003_video_web_viewer.png -------------------------------------------------------------------------------- /example_workflows/example_web_viewer_004_video_web_viewer_video_with_sfx.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/example_workflows/example_web_viewer_004_video_web_viewer_video_with_sfx.jpg -------------------------------------------------------------------------------- /example_workflows/example_web_viewer_004_video_web_viewer_video_with_sfx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/example_workflows/example_web_viewer_004_video_web_viewer_video_with_sfx.png -------------------------------------------------------------------------------- /example_workflows/example_web_viewer_005_audio_web_viewer_f5_tts.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/example_workflows/example_web_viewer_005_audio_web_viewer_f5_tts.jpg -------------------------------------------------------------------------------- /example_workflows/example_web_viewer_005_audio_web_viewer_f5_tts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/example_workflows/example_web_viewer_005_audio_web_viewer_f5_tts.png -------------------------------------------------------------------------------- /example_workflows/example_websocket_nodes_001_basic.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/example_workflows/example_websocket_nodes_001_basic.jpeg -------------------------------------------------------------------------------- /example_workflows/example_websocket_nodes_001_basic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/example_workflows/example_websocket_nodes_001_basic.png -------------------------------------------------------------------------------- /https/cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFozCCA4ugAwIBAgIUVVgc1Zg1/AfCZ795eaeBxngQRUIwDQYJKoZIhvcNAQEL 3 | BQAwYTELMAkGA1UEBhMCWFgxCzAJBgNVBAgMAlVLMQ8wDQYDVQQHDAZMb25kb24x 4 | EzARBgNVBAoMClZyY2hTdHVkaW8xDTALBgNVBAsMBFZyY2gxEDAOBgNVBAMMB3Zy 5 | Y2guYWkwHhcNMjQxMTA3MTg0ODIzWhcNMzQxMTA1MTg0ODIzWjBhMQswCQYDVQQG 6 | EwJYWDELMAkGA1UECAwCVUsxDzANBgNVBAcMBkxvbmRvbjETMBEGA1UECgwKVnJj 7 | aFN0dWRpbzENMAsGA1UECwwEVnJjaDEQMA4GA1UEAwwHdnJjaC5haTCCAiIwDQYJ 8 | KoZIhvcNAQEBBQADggIPADCCAgoCggIBAOrmJS+sRgZsgtxsxBgREdpnWYI28jDI 9 | eXyp6VbAE+OT0ClQO/McYUMZMDPzvbUwA51RRzpRFmCsQmhICf9PqDlJG3NY+cfB 10 | Mv6r0mzRnh8ehA8xezDpMTpAmx9hqfiXWrIrBnijeoLUlbSbhLWKFQLdgR5eUDqw 11 | 3FkBclG/6RS6oHi4iOkjLNWDgqthfwAWbwMfsxs/3ChP5Cm6q/RUzVZxq84NPzcM 12 | J/gHDNETD1pt6XqbJfeSu2L7xVOGbofvoD5JUICFiXYOnKVv0chRiEvQPHOsjHf3 13 | dMuGjDCR8ugtVrmINLb2YCveYkU8o+J1PD+3YCbcqn+qlnBgKUisLKuR8uraXRWF 14 | DzOfBGbGJmNrnwCZ8HzEv8no6922DFWaIVdbgujsHdJX6UGKGbS0FgZPBNozCKvm 15 | EOx3mNFtdPyWrAe8kbwyCHNVIRwyGD31AqvcxMhY21p2bkDF9nX3KQ3p/lqNbVyi 16 | qVJfBWbv/kizjDkUHZF+vXWnZ1+UjaIWuQchLdywO/p19TkrXabjibF8Vq/qge50 17 | wYpJKoqM+UzUo3ThG02id4wm3mAOWdg1XokDzrEaoYVVTKO5PkptdEUJ5F7n6W09 18 | LGdTQGgMPcJcSigKOakKZkyFk07rcVQAyqMJYarqn2mUT2zT1UoOYr0+TrwNnknw 19 | iN8grpZUloXxAgMBAAGjUzBRMB0GA1UdDgQWBBQR+N9VuxOCaYftBmSClyVj5fFZ 20 | +zAfBgNVHSMEGDAWgBQR+N9VuxOCaYftBmSClyVj5fFZ+zAPBgNVHRMBAf8EBTAD 21 | AQH/MA0GCSqGSIb3DQEBCwUAA4ICAQAQETd+AgAG0+1m++bnric/9Y/bfBXp7rCi 22 | wkeqHpHuuurIjt7lvj5FHd4XbkVLK/4xTbGpaUUT4+2wiYlg8guPnWPoWShxuAs6 23 | K4dRjtXjyIP80mkxi0QGF4DXf9s6v1VRuaXxW4a7ATzFZu5LkEKaUcQdpzYN3KN+ 24 | NrAQGvlp5QwkScjBcDdJvIQ7pnpT+9BWwlC23nWce7zHG37XYDY2GT18bwlrN3WQ 25 | hQQ89lRu6a+zYMx8dDWmQhH7ZXDXImz9GekIQKytoNPATyrSCaQs5vCjNhMWeXzz 26 | QRB/63MdnYNJTZid2sHHBIfViuDN9zhM81TWvtT5/6C48tmIElnbftEEKyWRw3Mz 27 | r4yX49crkyIRnIj1YptZwemiB6uxAhME2GhygtNeCe5mYi1CQIZCfTES5Dg5YF88 28 | an4nRduVN4dk61jXQ2L1ypUkHDLv7rnZd50oP0mwYDNmRhFMIVmvdC/9AElDnq7/ 29 | DKHWRmkoXGnwJn/CjtPeIiigV0buvXlCNFmJYuoOfwsZCP53HcX08jDMATYI5nnT 30 | zxbvCK0wTpI+2gpF8iu9had3Crz2BxXQnYWLffGUHumxuwXiANSePMsN7Ahwp/um 31 | XS9IAZGaXVljcejbzo+D5XsGi0fyJho1NQhkmOQLsSLFL54jezDAa6Jb7ldboSr1 32 | onF8nSVTqg== 33 | -----END CERTIFICATE----- 34 | -------------------------------------------------------------------------------- /https/key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQDq5iUvrEYGbILc 3 | bMQYERHaZ1mCNvIwyHl8qelWwBPjk9ApUDvzHGFDGTAz8721MAOdUUc6URZgrEJo 4 | SAn/T6g5SRtzWPnHwTL+q9Js0Z4fHoQPMXsw6TE6QJsfYan4l1qyKwZ4o3qC1JW0 5 | m4S1ihUC3YEeXlA6sNxZAXJRv+kUuqB4uIjpIyzVg4KrYX8AFm8DH7MbP9woT+Qp 6 | uqv0VM1WcavODT83DCf4BwzREw9abel6myX3krti+8VThm6H76A+SVCAhYl2Dpyl 7 | b9HIUYhL0DxzrIx393TLhowwkfLoLVa5iDS29mAr3mJFPKPidTw/t2Am3Kp/qpZw 8 | YClIrCyrkfLq2l0VhQ8znwRmxiZja58AmfB8xL/J6OvdtgxVmiFXW4Lo7B3SV+lB 9 | ihm0tBYGTwTaMwir5hDsd5jRbXT8lqwHvJG8MghzVSEcMhg99QKr3MTIWNtadm5A 10 | xfZ19ykN6f5ajW1coqlSXwVm7/5Is4w5FB2Rfr11p2dflI2iFrkHIS3csDv6dfU5 11 | K12m44mxfFav6oHudMGKSSqKjPlM1KN04RtNoneMJt5gDlnYNV6JA86xGqGFVUyj 12 | uT5KbXRFCeRe5+ltPSxnU0BoDD3CXEooCjmpCmZMhZNO63FUAMqjCWGq6p9plE9s 13 | 09VKDmK9Pk68DZ5J8IjfIK6WVJaF8QIDAQABAoICAAFxIHhkkgj4efdVARal5dfI 14 | U4sKX+NMsGc4yp6Dqb5CF5CQsM+Eb0noceHtwfk74QJ3v7cj994nzIq5P3t2WhcL 15 | mSF/VzLX/3fDUanlQazZT3r25Sr8PCG/s1wRKcg600hoSHRtiiMwX3YMetdGRdb+ 16 | yavoV7fRLbHBAc+ZPPAUQwd+rX4jNG4f7f7z53dLtp/R/jPW07jZIF0hC4IW/QGF 17 | 9jBQJ0D6StVZo2U1/QhYx/U+q3reXGwGVYmhLSpEiQBSNk5AU5h6jOZck3t2RFQ1 18 | JDvbCtaH2bW6nuoY6UEY2dE5fMZZ6JD4Xwl5A/IFdsqsKzOjM4uP/jdUyx1qYNCj 19 | 9AjP2tn0vTQpa6iWTPtMvi5GbkntezZrKyLFlr2EgT22vIxbgsWPWh5I5lOVMnar 20 | c0kE20XkgRrZbdgRNkFcxiN1cew78nld+YsvwrXjQ6gfhhNEGEa28rWczFtxdCgy 21 | 22UT/1SkZbmGAbEb/98uEmhcBxihi8xDvcpyHD0V9+uMj1DD0FJjIy/7WdwKXpfh 22 | FdUmKcaVjXlLhlcbxbZNyzZ6q+rlSzagpbRTRn5w5QkMVimfy8JJrv17/qpFbV5r 23 | x4jiUhmYO2DD7FzCMes9scjpuTWNMbguU52DMvJriGJ86oFAA4OdOYKb6EgI2o6G 24 | zLrH1HzHxsi6w4eGQlkJAoIBAQD+SHdRD5ZbnBWU1vlf3HFfMqmFE90AWHsfKg7U 25 | 5gCn3MootMKhgmsEcpLtpiz9Y/5V+/PWpcYm9Ojbg8TfziImHAGxVnLJY9yUV2gL 26 | xVreU4iUOIhdsX2VqtxtmHfVKkqFTkgTddeEvQb/TexOBjpI+sAXPcRArgdGC01h 27 | 0fWorGjtisGnOA0u4b6iIboZSd2f76b7p44mVU2wqJ4OSo840/HBRBT4fFW73ac0 28 | 0foHbV1cUFnpDZvkGZYLV3SrMDXU34f1qiR02zYK54CwCkeQzkImoj0tMSefBNOp 29 | Xuhv4YzeOK2dshU0L1rumVltoEzn+tYHDJX/oeC5i1jRhb7JAoIBAQDsfCxjXLsA 30 | TZvn4CiREQHk4CUpqWh4nVsB7wb5bfXC7vSRN3WczNXJjY6LF8YPs9behavb0Ohh 31 | bzRZRkoO+JqgZQcT7JwILqnSvfzRkY2YNPn28RY32v8f+EAYOIGbctPS0tkI1VwW 32 | BtwM4VM3T+Q9mLOnLiGgvYqeu4moNW/lwni4/IudOw5C6zTUsKHmMLcIJku5828v 33 | LZK+O0G4oBvq+RlncXfLMBtGpuhS82gZwLznchAN78/P/5ZmIx6QCY7wvryTmYPO 34 | JCwUYGydBdXhZS+fDXnEKg+UdsYKtVtudaDO+Q0XaUjE6IVyOtY2e7h4GoXEAXIE 35 | N5F57lGqSFnpAoIBAQDxda6Xcs28QALeSySSOqoNU/ZVP/9kDKEdfrqGv8FvpODy 36 | tnL7GvS0THfo4xtlokYpMA7jjJcU9wsHJ6ETRJJ4za9tzkFBq2oSEs3iH42iAIB4 37 | /xW+ooQ2UwDtsRJZgMcyvpsCn6v/h9/VHxF9JbzDABjX+Pca1CZkBOd+M/mqKc2Z 38 | g2FQgJFEK2jyXERzLwkcjl1qKjl5fRdA4Vz1BSfrNpcihpvDOY3PzzKvnZRm6Rr2 39 | RfaarymQ+Braujk3NZhog7p/NQlZTa+Luzkf8SL0Bmgad4TQHRUak2LKOi1N0EgL 40 | 3JZAYJJx82XNnCCnUMi2oCIMn47wJ69VgVTd4LrBAoIBAHbaqcx+jJXIa7UMWhce 41 | vx7y8HqR5CnKnDTKl2zBMSV5x308rGXUJRaZiKyrlCXMMw+/ivJ9em2N4HeF9i60 42 | D7LRU89Vuc369esvBWOmjc2TwpWz8YC9FkBRGpWRwMIJ4iAxTFc+NHv1kp7+mD5S 43 | btnuZ0x8yPXqjULd6n+Z69VD+jyu4NRzG+XSJMlIxPXWQgdch+67RIQnmXr2eT97 44 | aMydyYDN7HE4ilzmAhm+r/FRhx+jmiIHByD4yfQnbnLh+u9QTaK5Ix912pmbJLt+ 45 | dVJE8rwzn6FRqmbpkvtKztsjT2D2lvfPOH2Oh5MzsvnLEJl8awJp343zB7CKcIzu 46 | tBECggEAd6pezwhxx25yvEKzNuqDsNBl7f/wDVDgc6KSaN5sSWXjFWL3DcKbofPH 47 | ETJSc62QR4WUFCRTues/r/jVkiAE4bvkTbKVfYEIhslWFV2N1OIA92gNIPSXEUor 48 | BqfgV/41hyF3vgIAaRaryqkmC0I4UDiIRSD1mZZGzSbXzfZdICsw7tN9CyEConmH 49 | NhSE+udz5oIJwWwk3sP97Vwby8CmxFPhD/bKAQAjPdujsg/VmS6Pom5CQQb/LQaM 50 | S/W+r03rS4Ftbvq0AfD8qCKW/U9T7y+WWnnj4GxM5N7D6+4bZvVrdw4fBjvf6noy 51 | g80+ZM6Bh1fhaPuo0+tDKCtggK61zw== 52 | -----END PRIVATE KEY----- 53 | -------------------------------------------------------------------------------- /nodes/image_nodes.py: -------------------------------------------------------------------------------- 1 | import io, base64 2 | import os 3 | import torch 4 | import numpy as np 5 | from PIL import Image 6 | import folder_paths 7 | 8 | CATEGORY = "vrch.ai/image" 9 | 10 | class VrchImageSaverNode: 11 | @classmethod 12 | def INPUT_TYPES(s): 13 | return { 14 | "required": { 15 | "images": ("IMAGE",), 16 | "filename": ("STRING", {"default": "web_viewer_image"}), 17 | "path": ("STRING", {"default": "web_viewer"}), 18 | "extension": (["jpeg", "png", "webp"], {"default": "jpeg"}), 19 | }, 20 | "optional": { 21 | "quality_jpeg_or_webp": ("INT", {"default": 85, "min": 1, "max": 100}), 22 | "optimize_png": ("BOOLEAN", {"default": False}), 23 | "lossless_webp": ("BOOLEAN", {"default": True}), 24 | "enable_preview": ("BOOLEAN", {"default": False}), 25 | } 26 | } 27 | 28 | RETURN_TYPES = () 29 | FUNCTION = "save_images" 30 | OUTPUT_NODE = True 31 | CATEGORY = CATEGORY 32 | 33 | def __init__(self): 34 | self.output_dir = folder_paths.output_directory 35 | 36 | def save_images(self, images, filename, path, extension, quality_jpeg_or_webp=85, optimize_png=False, lossless_webp=True, enable_preview=False): 37 | output_path = os.path.join(self.output_dir, path) 38 | os.makedirs(output_path, exist_ok=True) 39 | 40 | results = [] 41 | for i, image in enumerate(images): 42 | if isinstance(image, torch.Tensor): 43 | image = image.cpu().numpy() 44 | img = Image.fromarray(np.clip(image * 255.0, 0, 255).astype(np.uint8)) 45 | 46 | file_name = f"{filename}_{i:02d}.{extension}" if len(images) > 1 else f"{filename}.{extension}" 47 | full_path = os.path.join(output_path, file_name) 48 | 49 | if extension == "png": 50 | img.save(full_path, optimize=optimize_png) 51 | elif extension == "webp": 52 | img.save(full_path, quality=quality_jpeg_or_webp, lossless=lossless_webp) 53 | else: # jpeg 54 | img.save(full_path, quality=quality_jpeg_or_webp, optimize=True) 55 | 56 | results.append({"filename": file_name, "subfolder": path, "type": "output"}) 57 | 58 | if enable_preview: 59 | return {"ui": {"images": results}} 60 | else: 61 | return {} 62 | 63 | 64 | class VrchImagePreviewBackgroundNode(VrchImageSaverNode): 65 | @classmethod 66 | def INPUT_TYPES(cls): 67 | return { 68 | "required": { 69 | "images": ("IMAGE",), 70 | "channel": (["1", "2", "3", "4", "5", "6", "7", "8"], {"default": "1"}), 71 | "background_display": ("BOOLEAN", {"default": True}), 72 | "refresh_interval_ms": ("INT", {"default": 300, "min": 50, "max": 10000}), 73 | "display_option": (["original", "fit", "stretch", "crop"], {"default": "fit"}), 74 | "batch_display": ("BOOLEAN", {"default": False}), 75 | "batch_display_interval_ms": ("INT", {"default": 200, "min": 50, "max": 10000}), 76 | "batch_images_size": ("INT", {"default": 4, "min": 1, "max": 100}), 77 | } 78 | } 79 | 80 | RETURN_TYPES = () 81 | RETURN_NAMES = () 82 | FUNCTION = "save_image_to_td_background" 83 | OUTPUT_NODE = True 84 | CATEGORY = CATEGORY 85 | 86 | def __init__(self): 87 | super().__init__() 88 | # By default, images are saved into ComfyUI's output directory 89 | self.output_dir = folder_paths.output_directory 90 | 91 | def save_image_to_td_background(self, 92 | images, 93 | channel, 94 | background_display, 95 | refresh_interval_ms, 96 | display_option, 97 | batch_display, 98 | batch_display_interval_ms, 99 | batch_images_size): 100 | 101 | # Save the images into "preview_background" directory with filename "{channel}_{index:%02d}.jpeg" 102 | filename = f"channel_{channel}" 103 | path = f"preview_background" 104 | 105 | output_path = os.path.join(self.output_dir, path) 106 | os.makedirs(output_path, exist_ok=True) 107 | 108 | self.save_images( 109 | images=images, 110 | filename=filename, 111 | path=path, 112 | extension="jpeg", 113 | quality_jpeg_or_webp=85, 114 | ) 115 | 116 | return () 117 | 118 | class VrchImagePreviewBackgroundNewNode: 119 | @classmethod 120 | def INPUT_TYPES(cls): 121 | return { 122 | "required": { 123 | "images": ("IMAGE",), 124 | "background_display": ("BOOLEAN", {"default": True}), 125 | "display_option": (["original", "fit", "stretch", "crop"], {"default": "fit"}), 126 | "batch_display": ("BOOLEAN", {"default": False}), 127 | "batch_display_interval_ms": ("INT", {"default": 200, "min": 50, "max": 10000}) 128 | }, 129 | } 130 | RETURN_TYPES = () 131 | RETURN_NAMES = () 132 | FUNCTION = "preview_images_to_ui" 133 | OUTPUT_NODE = True 134 | CATEGORY = CATEGORY 135 | 136 | def preview_images_to_ui(self, 137 | images, 138 | background_display=True, 139 | batch_display=False, 140 | batch_display_interval_ms=200, 141 | display_option="fit"): 142 | 143 | ui_images = [] 144 | for i, image in enumerate(images): 145 | if isinstance(image, torch.Tensor): 146 | image = image.cpu().numpy() 147 | img = Image.fromarray(np.clip(image * 255.0, 0, 255).astype(np.uint8)) 148 | buffer = io.BytesIO() 149 | img.save(buffer, format="JPEG", quality=85) 150 | jpg_bytes = buffer.getvalue() 151 | 152 | # Base64-encode and prepend data-URI header 153 | b64 = base64.b64encode(jpg_bytes).decode('utf-8') 154 | ui_images.append(f"data:image/jpeg;base64,{b64}") 155 | 156 | # If batch_display is False, only show the first image 157 | if not batch_display: 158 | ui_images = [ui_images[0]] if ui_images else [] 159 | 160 | # Send images, display option and enable flag to UI 161 | return { 162 | "ui": { 163 | "background_images": ui_images, 164 | "display_option": [display_option], 165 | "background_display": [background_display], 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /nodes/midi_nodes.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | import json 3 | 4 | CATEGORY = "vrch.ai/control/midi" 5 | 6 | class VrchMidiDeviceLoaderNode: 7 | @classmethod 8 | def INPUT_TYPES(s): 9 | return { 10 | "required": { 11 | "device_id": ("STRING", {"default": ""}), 12 | "name": ("STRING", {"default": ""}), 13 | "debug": ("BOOLEAN", {"default": False}), 14 | "raw_data": ("STRING", {"default": "", "multiline": True, "dynamicPrompts": False}), 15 | }, 16 | } 17 | 18 | RETURN_TYPES = ( 19 | "JSON", # RAW_DATA 20 | "INT", # INT_CC_VALUES 21 | "FLOAT", # FLOAT_CC_VALUES 22 | "BOOL", # BOOLEAN_NOTE_VALUES 23 | "INT", # INT_NOTE_VALUES 24 | ) 25 | 26 | RETURN_NAMES = ( 27 | "RAW_DATA", 28 | "INT_CC_VALUES", 29 | "FLOAT_CC_VALUES", 30 | "BOOLEAN_NOTE_VALUES", 31 | "INT_NOTE_VALUES", 32 | ) 33 | 34 | CATEGORY = CATEGORY 35 | FUNCTION = "load_midi_device" 36 | 37 | def load_midi_device(self, 38 | device_id: str, 39 | name: str, 40 | debug: bool=False, 41 | raw_data: str=""): 42 | """ 43 | Load MIDI device data and return it as JSON. 44 | """ 45 | try: 46 | # Parse raw_data as JSON 47 | midi_data = { 48 | "device_id": device_id, 49 | "name": name, 50 | "raw_data": raw_data, 51 | } 52 | 53 | # Initialize default values 54 | int_cc_values = [0] * 128 # Controller int values (0-127) 55 | float_cc_values = [0.0] * 128 # Controller float values (0.0-1.0) 56 | boolean_note_values = [False] * 128 # Note on/off status 57 | int_note_values = [0] * 128 # Note values (0-127) 58 | 59 | # Try to parse MIDI data 60 | if raw_data: 61 | try: 62 | parsed_data = json.loads(raw_data) 63 | 64 | # Extract CC controller data 65 | if "cc" in parsed_data and isinstance(parsed_data["cc"], list): 66 | for cc in parsed_data["cc"]: 67 | if "number" in cc and "value" in cc: 68 | cc_num = int(cc["number"]) 69 | cc_value = int(cc["value"]) 70 | if 0 <= cc_num < 128: 71 | int_cc_values[cc_num] = cc_value # Store raw int value (0-127) 72 | float_cc_values[cc_num] = float(cc_value) / 127.0 # Normalize to 0-1 73 | 74 | # Extract note data 75 | if "notes" in parsed_data and isinstance(parsed_data["notes"], list): 76 | for note in parsed_data["notes"]: 77 | if "number" in note and "velocity" in note and "status" in note: 78 | note_num = int(note["number"]) 79 | if 0 <= note_num < 128: 80 | int_note_values[note_num] = int(note["velocity"]) 81 | boolean_note_values[note_num] = note["status"] == "noteOn" 82 | 83 | except json.JSONDecodeError: 84 | if debug: 85 | print("[VrchMidiDeviceLoaderNode] Failed to parse MIDI data as JSON") 86 | except Exception as e: 87 | if debug: 88 | print(f"[VrchMidiDeviceLoaderNode] Error processing MIDI data: {str(e)}") 89 | 90 | if debug: 91 | print(f"[VrchMidiDeviceLoaderNode] MIDI data:", json.dumps(midi_data, indent=2, ensure_ascii=False)) 92 | print(f"[VrchMidiDeviceLoaderNode] INT_CC_VALUES sample: {int_cc_values[:5]}...") 93 | print(f"[VrchMidiDeviceLoaderNode] FLOAT_CC_VALUES sample: {float_cc_values[:5]}...") 94 | print(f"[VrchMidiDeviceLoaderNode] BOOLEAN_NOTE_VALUES sample: {boolean_note_values[:5]}...") 95 | print(f"[VrchMidiDeviceLoaderNode] INT_NOTE_VALUES sample: {int_note_values[:5]}...") 96 | 97 | return ( 98 | midi_data, 99 | int_cc_values, 100 | float_cc_values, 101 | boolean_note_values, 102 | int_note_values, 103 | ) 104 | 105 | except Exception as e: 106 | print(f"[VrchMidiDeviceLoaderNode] Error loading MIDI data: {str(e)}") 107 | # Return default values 108 | return ( 109 | {}, # RAW_DATA 110 | [0] * 128, # INT_CC_VALUES 111 | [0.0] * 128, # FLOAT_CC_VALUES 112 | [False] * 128, # BOOLEAN_NOTE_VALUES 113 | [0] * 128, # INT_NOTE_VALUES 114 | ) 115 | 116 | @classmethod 117 | def IS_CHANGED(cls, **kwargs): 118 | raw_data = kwargs.get("raw_data", "") 119 | debug = kwargs.get("debug", False) 120 | if not raw_data: 121 | if debug: 122 | print("[VrchMidiDeviceLoaderNode] No raw_data provided to IS_CHANGED.") 123 | return False 124 | 125 | m = hashlib.sha256() 126 | m.update(raw_data.encode("utf-8")) 127 | return m.hexdigest() -------------------------------------------------------------------------------- /nodes/node_utils.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | import socket 4 | 5 | class VrchNodeUtils: 6 | 7 | @staticmethod 8 | def get_default_ip_address(fallback_ip="127.0.0.1"): 9 | try: 10 | s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 11 | s.connect(("8.8.8.8", 80)) 12 | local_ip = s.getsockname()[0] 13 | s.close() 14 | return local_ip 15 | except Exception: 16 | return fallback_ip 17 | 18 | @staticmethod 19 | def remap(value, in_min=0.0, in_max=1.0, out_min=0.0, out_max=1.0): 20 | """ 21 | Remap a scalar value from the range [in_min, in_max] to [out_min, out_max]. 22 | """ 23 | if in_max == in_min: 24 | return out_min 25 | # Clamp the value within the input range 26 | value = max(min(value, in_max), in_min) 27 | # Perform the remapping 28 | return out_min + ((value - in_min) / (in_max - in_min)) * (out_max - out_min) 29 | 30 | @staticmethod 31 | def remap_invert(value, in_min=0.0, in_max=1.0, out_min=0.0, out_max=1.0): 32 | """ 33 | Invert and remap a scalar value from the range [in_min, in_max] to [out_min, out_max]. 34 | """ 35 | if in_max == in_min: 36 | return out_max 37 | # Clamp the value within the input range 38 | value = max(min(value, in_max), in_min) 39 | # Perform the inverted remapping 40 | return out_max - ((value - in_min) / (in_max - in_min)) * (out_max - out_min) 41 | 42 | @staticmethod 43 | def select_remap_func(invert: bool): 44 | return VrchNodeUtils.remap_invert if invert else VrchNodeUtils.remap 45 | 46 | @staticmethod 47 | def save_channel_settings(output_path, channel, settings_dict): 48 | """ 49 | Save settings for a channel to a JSON file. 50 | 51 | Args: 52 | output_path (str): Directory path to save the settings file 53 | channel (str): Channel identifier 54 | settings_dict (dict): Dictionary containing the settings 55 | """ 56 | settings_filename = f"channel_{channel}_settings.json" 57 | settings_path = os.path.join(output_path, settings_filename) 58 | with open(settings_path, "w") as f: 59 | json.dump(settings_dict, f) -------------------------------------------------------------------------------- /nodes/test/osc_server_test.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | from pythonosc import dispatcher, osc_server 3 | import threading 4 | 5 | def handle_osc_message(address, args, value): 6 | debug = args[0].get("debug", False) if isinstance(args[0], dict) else False 7 | if debug: 8 | print(f"Received OSC message: addr={address}, value={value}") 9 | print(f"{address}: {value}") 10 | 11 | def setup_osc_server(ip, port, path, debug=False): 12 | dispatcher_instance = dispatcher.Dispatcher() 13 | dispatcher_instance.map(f"{path}", handle_osc_message, {"debug": debug}) 14 | 15 | server = osc_server.ThreadingOSCUDPServer((ip, port), dispatcher_instance) 16 | server_thread = threading.Thread(target=server.serve_forever) 17 | server_thread.start() 18 | print(f"OSC server is running at {ip}:{port} and listening on path {path}") 19 | return server, server_thread 20 | 21 | if __name__ == "__main__": 22 | parser = argparse.ArgumentParser(description="OSC Server Test Script (Press CTRL+C to exit)") 23 | parser.add_argument("--ip", type=str, default="127.0.0.1", help="IP address to listen on") 24 | parser.add_argument("--port", type=int, default=8000, help="Port to listen on") 25 | parser.add_argument("--path", type=str, required=True, help="OSC path to listen to (required)") 26 | parser.add_argument("--debug", action="store_true", help="Enable debug mode") 27 | 28 | args = parser.parse_args() 29 | 30 | server, server_thread = setup_osc_server(args.ip, args.port, args.path, args.debug) 31 | 32 | try: 33 | while True: 34 | pass # Keep the script running 35 | except KeyboardInterrupt: 36 | print("\nShutting down OSC server...") 37 | server.shutdown() 38 | server.server_close() 39 | server_thread.join() 40 | print("OSC server shut down.") -------------------------------------------------------------------------------- /nodes/text_nodes.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | import json 3 | import requests 4 | import srt 5 | from datetime import timedelta 6 | import traceback 7 | 8 | CATEGORY="vrch.ai/text" 9 | 10 | class VrchJsonUrlLoaderNode: 11 | @classmethod 12 | def INPUT_TYPES(s): 13 | return { 14 | "required": { 15 | "url": ("STRING", {"default": ""}), 16 | }, 17 | "optional": { 18 | "print_to_console": ("BOOLEAN", {"default": False}), 19 | }, 20 | } 21 | 22 | RETURN_TYPES = ("JSON",) 23 | CATEGORY = CATEGORY 24 | FUNCTION = "load_json" 25 | 26 | def load_json(self, url: str, print_to_console=False): 27 | try: 28 | response = requests.get(url, timeout=5) 29 | response.raise_for_status() # This will raise an HTTPError for bad responses 30 | 31 | res = response.json() # Attempt to parse JSON 32 | 33 | if print_to_console: 34 | print("JSON content:", json.dumps(res, indent=2, ensure_ascii=False)) 35 | 36 | except requests.RequestException as e: 37 | print(f"Request failed: {str(e)}") 38 | res = {} 39 | except json.JSONDecodeError as e: 40 | print(f"Invalid JSON: {str(e)}") 41 | res = {} 42 | except Exception as e: 43 | print(f"An unexpected error occurred: {str(e)}") 44 | res = {} 45 | 46 | return (res,) 47 | 48 | 49 | class VrchTextSrtPlayerNode: 50 | @classmethod 51 | def INPUT_TYPES(s): 52 | return { 53 | "required": { 54 | "srt_text": ("STRING", {"default": "", "multiline": True, "dynamicPrompts": False}), 55 | "placeholder_text": ("STRING", {"default": "", "multiline": False, "dynamicPrompts": False}), 56 | "loop": ("BOOLEAN", {"default": False}), 57 | "current_selection": ("INT", {"default": 1}), 58 | "debug": ("BOOLEAN", {"default": False}), 59 | }, 60 | } 61 | 62 | RETURN_TYPES = ("STRING",) 63 | RETURN_NAMES = ("TEXT",) 64 | CATEGORY = CATEGORY 65 | FUNCTION = "play_srt_text" 66 | 67 | def play_srt_text(self, 68 | srt_text: str, 69 | placeholder_text: str="", 70 | loop: bool=False, 71 | current_selection: int=0, 72 | debug: bool=False): 73 | try: 74 | if debug: 75 | print("[VrchTextSrtPlayerNode] Playing SRT Text:", srt_text) 76 | 77 | # use -1 as a flag for no selection output 78 | if current_selection == -1: 79 | return (placeholder_text,) 80 | 81 | # Use srt python lib to parse srt text 82 | srt_entries = list(srt.parse(srt_text)) 83 | 84 | if current_selection < 1 or current_selection > len(srt_entries): 85 | raise IndexError("Current selection index out of range") 86 | 87 | selected_text = srt_entries[current_selection-1].content 88 | 89 | if debug: 90 | print(f"[VrchTextSrtPlayerNode] Selected SRT Entry [{current_selection}]: {selected_text}") 91 | 92 | return (selected_text,) 93 | 94 | except Exception as e: 95 | callsite = traceback.extract_stack()[-2] 96 | error_message = f"[VrchTextSrtPlayerNode] An error occurred when calling play_srt_text(): {str(e)} at {callsite.filename.split('/')[-1]}:{callsite.lineno}" 97 | print(error_message) 98 | raise ValueError(error_message) 99 | 100 | @classmethod 101 | def IS_CHANGED(cls, 102 | srt_text: str, 103 | placeholder_text: str, 104 | loop: bool, 105 | current_selection: int, 106 | debug: bool): 107 | m = hashlib.sha256() 108 | m.update(srt_text.encode('utf-8')) 109 | m.update(placeholder_text.encode('utf-8')) 110 | m.update(str(loop).encode('utf-8')) 111 | m.update(str(current_selection).encode('utf-8')) 112 | return m.hexdigest() -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "comfyui-web-viewer" 3 | description = "The ComfyUI Web Viewer by vrch.ai is a custom node collection offering a real-time AI-generated interactive art framework. This utility integrates realtime streaming into ComfyUI workflows, supporting keyboard control nodes, OSC control nodes, sound input nodes, and more. Accessible from any device with a web browser, it enables real time interaction with AI-generated content, making it ideal for interactive visual projects and enhancing ComfyUI workflows with efficient content management and display." 4 | version = "1.0.33" 5 | license = {file = "LICENSE"} 6 | dependencies = ["aiohttp","ffmpeg-python","pydub","python-osc","scikit-learn","matplotlib"] 7 | 8 | [project.urls] 9 | Repository = "https://github.com/VrchStudio/comfyui-web-viewer" 10 | # Used by Comfy Registry https://comfyregistry.org 11 | 12 | [tool.comfy] 13 | PublisherId = "vrchdev" 14 | DisplayName = "ComfyUI Web Viewer" 15 | Icon = "https://vrch.ai/images/vrch_Logo_vertical_black.png" -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | aiohttp 2 | ffmpeg-python 3 | matplotlib 4 | pydub 5 | python-osc 6 | scikit-learn 7 | srt 8 | websockets -------------------------------------------------------------------------------- /update_version.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import sys 3 | 4 | def update_version(version_part): 5 | try: 6 | subprocess.run(['bump2version', version_part], check=True) 7 | print(f"Version updated successfully. New version: {get_current_version()}") 8 | except subprocess.CalledProcessError as e: 9 | print(f"Error updating version: {e}") 10 | sys.exit(1) 11 | 12 | def get_current_version(): 13 | with open('__init__.py', 'r') as f: 14 | for line in f: 15 | if line.startswith('__version__'): 16 | return line.split('=')[1].strip().strip('"') 17 | return "Unknown" 18 | 19 | if __name__ == "__main__": 20 | if len(sys.argv) != 2 or sys.argv[1] not in ['major', 'minor', 'patch']: 21 | print("Usage: python update_version.py [major|minor|patch]") 22 | sys.exit(1) 23 | 24 | update_version(sys.argv[1]) -------------------------------------------------------------------------------- /utils/genres.json: -------------------------------------------------------------------------------- 1 | ["blues", "classical", "country", "disco", "hiphop", "jazz", "metal", "pop", "reggae", "rock"] -------------------------------------------------------------------------------- /web/comfyui/logic_nodes.js: -------------------------------------------------------------------------------- 1 | import { app } from "../../scripts/app.js"; 2 | import { api } from "../../scripts/api.js"; 3 | 4 | /** 5 | * Create a non‑clickable circular indicator element with a label. 6 | * @param {number} id – the indicator number to display 7 | * @returns {HTMLElement} 8 | */ 9 | function createTriggerToggleIndicatorElement(id = 1) { 10 | const indicator = document.createElement("div"); 11 | indicator.textContent = String(id); 12 | indicator.classList.add("vrch-trigger-indicator", "off"); 13 | indicator.style.pointerEvents = "none"; 14 | return indicator; 15 | } 16 | 17 | /** 18 | * Create multiple indicators and append them into a container. 19 | * @param {HTMLElement} container – parent element to hold indicators 20 | * @param {number} count – number of indicators to create 21 | * @returns {HTMLElement[]} 22 | */ 23 | function createTriggerToggleIndicators(container, count = 1) { 24 | const indicators = []; 25 | for (let i = 1; i <= count; i++) { 26 | const elt = createTriggerToggleIndicatorElement(i); 27 | container.appendChild(elt); 28 | indicators.push(elt); 29 | } 30 | return indicators; 31 | } 32 | 33 | app.registerExtension({ 34 | name: "vrch.TriggerToggleNodes", 35 | async nodeCreated(node) { 36 | if (node.comfyClass === "VrchTriggerToggleNode") { 37 | // create a container to hold all indicators 38 | const container = document.createElement("div"); 39 | container.classList.add("vrch-trigger-indicators-container"); 40 | node.addDOMWidget("indicator_container", "Indicators", container); 41 | 42 | // initialize indicators (default count = 1, can be extended) 43 | const stateIndicators = createTriggerToggleIndicators(container, 1); 44 | 45 | node.onExecuted = function(message) { 46 | const states = message.current_state; // array of booleans 47 | const ids = message.ui?.indicator_id ?? []; 48 | 49 | states.forEach((state, index) => { 50 | const indicator = stateIndicators[index]; 51 | const id = ids[index] ?? (index + 1); 52 | indicator.textContent = String(id); 53 | indicator.classList.toggle("on", !!state); 54 | indicator.classList.toggle("off", !state); 55 | }); 56 | }; 57 | } 58 | 59 | if (node.comfyClass === "VrchTriggerToggleX4Node") { 60 | // create a container to hold all indicators 61 | const container = document.createElement("div"); 62 | container.classList.add("vrch-trigger-indicators-container"); 63 | node.addDOMWidget("indicator_container", "Indicators", container); 64 | 65 | // initialize indicators (default count = 4) 66 | const stateIndicators = createTriggerToggleIndicators(container, 4); 67 | 68 | node.onExecuted = function(message) { 69 | const states = message.current_state; // array of booleans 70 | const ids = message.ui?.indicator_id ?? []; 71 | 72 | states.forEach((state, index) => { 73 | const indicator = stateIndicators[index]; 74 | const id = ids[index] ?? (index + 1); 75 | indicator.textContent = String(id); 76 | indicator.classList.toggle("on", !!state); 77 | indicator.classList.toggle("off", !state); 78 | }); 79 | }; 80 | } 81 | 82 | if (node.comfyClass === "VrchTriggerToggleX8Node") { 83 | // create a container to hold all indicators 84 | const container = document.createElement("div"); 85 | container.classList.add("vrch-trigger-indicators-container"); 86 | node.addDOMWidget("indicator_container", "Indicators", container); 87 | 88 | // initialize indicators (default count = 8) 89 | const stateIndicators = createTriggerToggleIndicators(container, 8); 90 | 91 | node.onExecuted = function(message) { 92 | const states = message.current_state; // array of booleans 93 | const ids = message.ui?.indicator_id ?? []; 94 | 95 | states.forEach((state, index) => { 96 | const indicator = stateIndicators[index]; 97 | const id = ids[index] ?? (index + 1); 98 | indicator.textContent = String(id); 99 | indicator.classList.toggle("on", !!state); 100 | indicator.classList.toggle("off", !state); 101 | }); 102 | }; 103 | } 104 | } 105 | }); 106 | 107 | // inject stylesheet for the container + circular indicators 108 | const style = document.createElement("style"); 109 | style.textContent = ` 110 | .vrch-trigger-indicators-container { 111 | display: flex; 112 | flex-wrap: wrap; 113 | justify-content: center; 114 | align-items: center; 115 | gap: 8px; 116 | padding: 4px; 117 | } 118 | .vrch-trigger-indicator { 119 | width: 32px; 120 | height: 32px; 121 | border-radius: 50%; 122 | display: flex; 123 | align-items: center; 124 | justify-content: center; 125 | font-size: 14px; 126 | font-weight: bold; 127 | transition: background-color 0.3s; 128 | user-select: none; 129 | } 130 | .vrch-trigger-indicator.off { 131 | background-color: #ccc; 132 | color: #333; 133 | } 134 | .vrch-trigger-indicator.on { 135 | background-color: #4CAF50; 136 | color: #fff; 137 | }`; 138 | document.head.appendChild(style); -------------------------------------------------------------------------------- /web/comfyui/node_utils.js: -------------------------------------------------------------------------------- 1 | // Enable debug logging 2 | export const ENABLE_DEBUG = false; 3 | 4 | // Helper functions to hide and show widgets 5 | export function hideWidget(node, widget) { 6 | // If widget is already hidden, do nothing 7 | if (widget.type === "hidden") return; 8 | // Save original type and computeSize so it can be restored later 9 | widget.origType = widget.type; 10 | widget.origComputeSize = widget.computeSize; 11 | widget.type = "hidden"; 12 | } 13 | 14 | export function showWidget(node, widget) { 15 | // Restore the widget's original type and computeSize 16 | widget.type = widget.origType; 17 | widget.computeSize = widget.origComputeSize; 18 | } 19 | 20 | /** 21 | * Helper function to build the URL based on configuration. 22 | * 23 | * @param {Object} config - The configuration object with the following properties: 24 | * serverWidget: widget for server address 25 | * sslWidget: widget for SSL flag (boolean) 26 | * extraParamsWidget: widget for extra parameters (string) 27 | * mode: string specifying the viewer mode (e.g., "image", "video", etc.) 28 | * path: string specifying the path (default is "web_viewer") 29 | * protocol: (optional) protocol to use (default is "http", also support "websocket") 30 | * channel: (optional) channel number (default is "1", [1-8] based) 31 | * fileGenerator: a function that returns the filename (receives the config as argument) 32 | * additionalParams: (optional) extra query parameters as an object (keys with widget values) or string 33 | * 34 | * @returns {string} The constructed URL. 35 | */ 36 | export function buildUrl(config) { 37 | // Retrieve server address and SSL flag (with default values) 38 | const server = config.serverWidget ? config.serverWidget.value : "127.0.0.1:8188"; 39 | const ssl = config.sslWidget ? config.sslWidget.value : false; 40 | const sslStr = ssl ? "true" : "false"; 41 | const scheme = ssl ? "https" : "http"; 42 | 43 | // Process extra parameters; if not empty and not starting with '&', prepend '&' 44 | let extraParams = config.extraParamsWidget ? config.extraParamsWidget.value : ""; 45 | if (extraParams && extraParams[0] !== "&") { 46 | extraParams = "&" + extraParams; 47 | } 48 | 49 | // Use provided mode and path (with a default for path) 50 | const mode = config.mode || ""; 51 | const path = config.path || "web_viewer"; 52 | const pathStr = path ? `&path=${path}` : ""; 53 | 54 | // Generate the filename using the provided fileGenerator callback 55 | const filename = config.fileGenerator ? config.fileGenerator(config) : ""; 56 | const filenameStr = filename ? `&filename=${filename}` : ""; 57 | 58 | // Process additional query parameters if provided 59 | let additionalQuery = ""; 60 | if (config.additionalParams && typeof config.additionalParams === "object") { 61 | // Assume each value in the object is a widget; extract its value 62 | additionalQuery = Object.entries(config.additionalParams) 63 | .map(([key, widget]) => `&${key}=${widget.value}`) 64 | .join(""); 65 | } else if (typeof config.additionalParams === "string") { 66 | additionalQuery = "&" + config.additionalParams; 67 | } 68 | 69 | let protocolStr = ""; 70 | let channelStr = ""; 71 | // If config.protocol is not http, insert the protocol in the URL before the server 72 | if (config.protocol) { 73 | protocolStr = `&protocol=${config.protocol}`; 74 | } 75 | if (config.channel) { 76 | channelStr = `&channel=${config.channel}`; 77 | } 78 | // Construct the URL using the provided parameters 79 | const url = `${scheme}://vrch.ai/viewer?mode=${mode}&server=${server}&ssl=${sslStr}${protocolStr}${channelStr}${filenameStr}${pathStr}${extraParams}${additionalQuery}`; 80 | 81 | // Return the final URL 82 | return url; 83 | } 84 | 85 | /** 86 | * Helper function to initialize the URL and widget visibility after a delay. 87 | * 88 | * @param {Object} node - The current node. 89 | * @param {Object} showUrlWidget - The widget controlling URL visibility. 90 | * @param {Object} urlWidget - The URL widget. 91 | * @param {Function} updateUrl - The updateUrl callback. 92 | */ 93 | export function delayInit(node, showUrlWidget, urlWidget, updateUrl) { 94 | setTimeout(() => { 95 | if (showUrlWidget) { 96 | showUrlWidget.value ? showWidget(node, urlWidget) : hideWidget(node, urlWidget); 97 | } 98 | updateUrl(); 99 | }, 1000); 100 | } 101 | 102 | /** 103 | * Helper function to create the "Open Web Viewer" button. 104 | * 105 | * @param {Object} node - The current node. 106 | * @param {Object} urlWidget - The widget containing the URL. 107 | * @param {Object} widthWidget - The widget for window width. 108 | * @param {Object} heightWidget - The widget for window height. 109 | */ 110 | export function createOpenWebViewerButton(node, urlWidget, widthWidget, heightWidget) { 111 | const button = document.createElement("button"); 112 | button.textContent = "Open Web Viewer"; 113 | button.classList.add("vrch-big-button"); 114 | button.onclick = () => { 115 | if (urlWidget && urlWidget.value) { 116 | const width = widthWidget ? widthWidget.value : 1280; 117 | const height = heightWidget ? heightWidget.value : 960; 118 | window.open(urlWidget.value, "_blank", `width=${width},height=${height}`); 119 | } else { 120 | console.error("URL widget not found or empty"); 121 | } 122 | }; 123 | node.addDOMWidget("button_widget", "Open Web Viewer", button); 124 | } 125 | 126 | /** 127 | * Helper function to set up widget callbacks for updating the URL and toggling visibility. 128 | * 129 | * @param {Object} node - The current node. 130 | * @param {Function} updateUrl - The updateUrl function to be called on widget change. 131 | * @param {Object} urlWidget - The URL widget to be toggled. 132 | * @param {Object} showUrlWidget - The widget controlling URL visibility. 133 | * @param {Array} widgets - An array of widgets whose callbacks will trigger updateUrl. 134 | * @param {string} [logPrefix] - (Optional) A prefix to log for debugging. 135 | */ 136 | export function setupWidgetCallback(node, updateUrl, urlWidget, showUrlWidget, widgets, logPrefix) { 137 | widgets.forEach(widget => { 138 | if (widget) { 139 | widget.callback = () => { 140 | if (logPrefix && ENABLE_DEBUG) { 141 | console.log(`${logPrefix}:::${widget.name}`); 142 | } 143 | updateUrl(); 144 | }; 145 | } 146 | }); 147 | if (showUrlWidget) { 148 | showUrlWidget.callback = (value) => { 149 | if (value) { 150 | showWidget(node, urlWidget); 151 | } else { 152 | hideWidget(node, urlWidget); 153 | } 154 | }; 155 | } 156 | } -------------------------------------------------------------------------------- /web/comfyui/websocket_nodes.js: -------------------------------------------------------------------------------- 1 | import { app } from "../../scripts/app.js"; 2 | import { ComfyWidgets } from '../../../scripts/widgets.js'; 3 | 4 | import { 5 | hideWidget, 6 | showWidget, 7 | buildUrl, 8 | delayInit, 9 | createOpenWebViewerButton, 10 | setupWidgetCallback, 11 | } from "./node_utils.js"; 12 | 13 | // ===================================================================== 14 | // Helper: Create Server Status Indicator Element 15 | // ===================================================================== 16 | /** 17 | * Create a non‑clickable circular indicator element for server status. 18 | * @returns {HTMLElement} 19 | */ 20 | function createServerStatusIndicatorElement() { 21 | const indicator = document.createElement("div"); 22 | indicator.textContent = "Run to Start Server"; // Initial text 23 | indicator.classList.add("vrch-server-indicator", "off"); // Start as off 24 | indicator.style.pointerEvents = "none"; 25 | return indicator; 26 | } 27 | 28 | // ===================================================================== 29 | // Extension: vrch.ImageWebSocketWebViewer 30 | // ===================================================================== 31 | app.registerExtension({ 32 | name: "vrch.ImageWebSocketWebViewer", 33 | async nodeCreated(node) { 34 | if (node.comfyClass === "VrchImageWebSocketWebViewerNode") { 35 | // Find existing widgets 36 | const serverWidget = node.widgets.find(w => w.name === "server"); 37 | const channelWidget = node.widgets.find(w => w.name === "channel"); 38 | const numberOfImagesWidget = node.widgets.find(w => w.name === "number_of_images"); 39 | const imageDisplayDurationWidget = node.widgets.find(w => w.name === "image_display_duration"); 40 | const fadeAnimDurationWidget = node.widgets.find(w => w.name === "fade_anim_duration"); 41 | const blendModeWidget = node.widgets.find(w => w.name === "blend_mode"); 42 | const loopPlaybackWidget = node.widgets.find(w => w.name === "loop_playback"); 43 | const updateOnEndWidget = node.widgets.find(w => w.name === "update_on_end"); 44 | const backgroundColorWidget = node.widgets.find(w => w.name === "background_colour_hex"); 45 | const widthWidget = node.widgets.find(w => w.name === "window_width"); 46 | const heightWidget = node.widgets.find(w => w.name === "window_height"); 47 | const extraParamsWidget = node.widgets.find(w => w.name === "extra_params"); 48 | const urlWidget = node.widgets.find(w => w.name === "url"); 49 | const showUrlWidget = node.widgets.find(w => w.name === "show_url"); 50 | 51 | function updateUrl() { 52 | if (urlWidget) { 53 | urlWidget.value = buildUrl({ 54 | serverWidget: serverWidget, 55 | extraParamsWidget: extraParamsWidget, 56 | mode: "image-websocket", 57 | protocol: "websocket", 58 | additionalParams: { 59 | channel: channelWidget, 60 | numberOfImages: numberOfImagesWidget, 61 | imageDisplayDuration: imageDisplayDurationWidget, 62 | fadeAnimDuration: fadeAnimDurationWidget, 63 | mixBlendMode: blendModeWidget, 64 | enableLoop: loopPlaybackWidget, 65 | enableUpdateOnEnd: updateOnEndWidget, 66 | bgColourPicker: backgroundColorWidget, 67 | } 68 | }); 69 | } 70 | } 71 | 72 | // Use setupWidgetCallback() with a custom log prefix if desired 73 | setupWidgetCallback( 74 | node, 75 | updateUrl, 76 | urlWidget, 77 | showUrlWidget, 78 | [ 79 | serverWidget, 80 | channelWidget, 81 | extraParamsWidget, 82 | numberOfImagesWidget, 83 | imageDisplayDurationWidget, 84 | fadeAnimDurationWidget, 85 | blendModeWidget, 86 | loopPlaybackWidget, 87 | updateOnEndWidget, 88 | backgroundColorWidget, 89 | ], 90 | "VrchImageWebSocketWebViewerNode" 91 | ); 92 | 93 | hideWidget(node, urlWidget); 94 | createOpenWebViewerButton(node, urlWidget, widthWidget, heightWidget); 95 | delayInit(node, showUrlWidget, urlWidget, updateUrl); 96 | } 97 | } 98 | }); 99 | 100 | // ===================================================================== 101 | // Extension: vrch.WebSocketServer 102 | // ===================================================================== 103 | app.registerExtension({ 104 | name: "vrch.WebSocketServer", 105 | async nodeCreated(node) { 106 | if (node.comfyClass === "VrchWebSocketServerNode") { 107 | // Find the debug widget to access its current value 108 | const debugWidget = node.widgets.find(w => w.name === "debug"); 109 | 110 | // Create a container for the indicator 111 | const container = document.createElement("div"); 112 | container.classList.add("vrch-server-indicator-container"); 113 | node.addDOMWidget("indicator_container", "Status", container); 114 | 115 | // Initialize the indicator 116 | const statusIndicator = createServerStatusIndicatorElement(); 117 | container.appendChild(statusIndicator); 118 | 119 | // Update indicator based on execution results 120 | const onExecuted = node.onExecuted; 121 | node.onExecuted = function(message) { 122 | onExecuted?.apply(this, arguments); 123 | 124 | // Get server status and debug status from message 125 | const isRunning = message?.server_status?.[0]; 126 | const isDebugEnabled = message?.debug_status?.[0]; 127 | 128 | if (typeof isRunning !== 'undefined') { 129 | statusIndicator.textContent = isRunning ? "Running" : "Stopped"; 130 | statusIndicator.classList.toggle("on", !!isRunning); 131 | statusIndicator.classList.toggle("off", !isRunning); 132 | 133 | // Log status update if debug is enabled 134 | if (isDebugEnabled) { 135 | console.log(`[VrchWebSocketServerNode] Server status update: ${isRunning ? 'Running' : 'Stopped'}`); 136 | } 137 | } 138 | 139 | // If there's an error message from path validation, it would be handled by ComfyUI's error system 140 | // We can add extra debug logging here if needed 141 | if (isDebugEnabled && typeof isRunning === 'undefined') { 142 | console.log("[VrchWebSocketServerNode] Executed but no status received:", message); 143 | } 144 | }; 145 | } 146 | } 147 | }); 148 | 149 | // Add custom styles for the button and indicator 150 | const style = document.createElement("style"); 151 | style.textContent = ` 152 | .vrch-big-button { 153 | background-color: #4CAF50; 154 | color: white; 155 | font-size: 16px; 156 | font-weight: bold; 157 | border: none; 158 | border-radius: 8px; 159 | cursor: pointer; 160 | text-align: center; 161 | transition: background-color 0.3s, transform 0.2s; 162 | } 163 | 164 | .vrch-big-button:hover { 165 | background-color: #45a049; 166 | transform: scale(1.05); 167 | } 168 | 169 | .vrch-big-button:active { 170 | background-color: #3e8e41; 171 | transform: scale(1); 172 | } 173 | 174 | /* Styles for Server Status Indicator */ 175 | .vrch-server-indicator-container { 176 | display: flex; 177 | justify-content: center; 178 | align-items: center; 179 | padding: 4px; 180 | } 181 | .vrch-server-indicator { 182 | width: 200px; 183 | height: 32px; 184 | border-radius: 16px; /* Pill shape */ 185 | display: flex; 186 | align-items: center; 187 | justify-content: center; 188 | font-size: 14px; 189 | font-weight: bold; 190 | transition: background-color 0.3s, color 0.3s; 191 | user-select: none; 192 | border: 1px solid #555; /* Add border */ 193 | } 194 | .vrch-server-indicator.off { 195 | background-color: #ccc; 196 | color: #333; 197 | } 198 | .vrch-server-indicator.on { 199 | background-color: #4CAF50; /* Green */ 200 | color: #fff; 201 | } 202 | `; 203 | document.head.appendChild(style); -------------------------------------------------------------------------------- /web/css/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | display: flex; 3 | justify-content: center; 4 | align-items: center; 5 | height: 100vh; 6 | margin: 0; 7 | background-color: #f0f0f0; 8 | } 9 | 10 | #image-container { 11 | max-width: 90%; 12 | max-height: 90%; 13 | } 14 | 15 | img { 16 | max-width: 100%; 17 | max-height: 100%; 18 | object-fit: contain; 19 | } -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ComfyUI Web Viewer 7 | 8 | 9 | 10 |
11 | Generated Image 12 |
13 | 14 | 15 | -------------------------------------------------------------------------------- /web/scripts/script.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VrchStudio/comfyui-web-viewer/027bb646c5e948288d26d38cb3242be37a325bc4/web/scripts/script.js --------------------------------------------------------------------------------