मुख्य सामग्री पर जाएं

Qwen 3.5 स्मॉल मॉडल्स ट्यूटोरियल: Ollama के साथ वीडियो‑से‑गेम जेनरेटर बनाएं

जानें कि Ollama के जरिए स्थानीय रूप से Qwen 3.5 स्मॉल मॉडल्स का उपयोग करके एक छोटे गेमप्ले क्लिप को खेलने योग्य HTML गेम में कैसे बदलें।
अपडेट किया गया 25 सित॰ 2026  · 14 मि॰ पढ़ें

AI के साथ खोजें

ChatGPTClaudePerplexity

Qwen 3.5 ओपन‑सोर्स मल्टीमॉडल मॉडलों का एक परिवार है जिसे पाठ और दृश्य दोनों इनपुट संभालने के लिए बनाया गया है। ये मॉडल इमेज‑टेक्स्ट‑टू‑टेक्स्ट आर्किटेक्चर का पालन करते हैं, जिससे वे प्राकृतिक भाषा प्रॉम्प्ट्स के साथ छवियों या वीडियो फ्रेम्स पर विचार‑विमर्श कर सकते हैं। 

इस ट्यूटोरियल में, हम एक Streamlit ऐप बनाएंगे जो गेमप्ले वीडियो को एक खेलने योग्य ब्राउज़र गेम में बदलता है, स्थानीय Qwen3.5 स्मॉल, qwen3.5:9b मॉडल का उपयोग करते हुए, जिसे Ollama के जरिए होस्ट किया गया है। यह एप्लिकेशन अपलोड किए गए वीडियो से फ्रेम निकालता है, गेम मैकेनिक्स का अनुमान लगाता है, उन्हें एक संरचित स्पेसिफिकेशन में बदलता है, और फिर एक पूरा HTML/CSS/JavaScript गेम जनरेट करता है जिसे सीधे इंटरफेस के अंदर प्रीव्यू किया जा सकता है।

इस दौरान, आप सीखेंगे कि:

  • मल्टीमॉडल रीजनिंग के लिए वीडियो से प्रतिनिधि फ्रेम निकालना
  • इमेज‑टेक्स्ट इन्फरेंस के लिए Ollama के साथ स्थानीय रूप से Qwen 3.5 चलाना
  • रीजनिंग और कोड जनरेशन के लिए दो‑चरणीय प्रॉम्प्टिंग पाइपलाइन डिज़ाइन करना
  • दृश्य गेमप्ले अवलोकनों को एक संरचित गेम स्पेसिफिकेशन में बदलना
  • अनुमानित मैकेनिक्स से एक खेलने योग्य HTML5 ब्राउज़र गेम जनरेट करना
  • पाइपलाइन चलाने और प्रीव्यू करने के लिए एक इंटरैक्टिव Streamlit इंटरफेस बनाना

अंत तक, आपके पास एक काम करने वाला डेमो होगा जो एक छोटे गेमप्ले क्लिप को पूरी तरह स्थानीय मशीन पर जनरेट किए गए, सरल और खेलने योग्य ब्राउज़र गेम में बदल देता है। आप नीचे दिए गए वीडियो में इस वर्कफ़्लो का संक्षिप्त संस्करण देख सकते हैं:

Qwen 3.5 Small क्या है?

Qwen 3.5 एक मल्टीमॉडल मॉडल परिवार है, जिसे हल्के एज मॉडल से लेकर फ्रंटियर‑स्केल रीजनिंग सिस्टम तक, विविध डिप्लॉयमेंट परिदृश्यों को कवर करने के लिए डिज़ाइन किया गया है। 

आधिकारिक Qwen 3.5 लाइनअप में इस समय 0.8B, 2B, 4B, 9B, 27B, 35B-A3B, 122B-A10B, और 397B-A17B वेरिएंट शामिल हैं, साथ ही कुछ मॉडलों के लिए बेस और क्वांटाइज़्ड चेकपॉइंट भी उपलब्ध हैं। 

सही स्मॉल मॉडल का चुनाव इस बात पर निर्भर करता है कि आप किस तरह का वर्कफ़्लो बनाना चाहते हैं। 

  • 0.8B और 2B वेरिएंट एज डिवाइस, रैपिड प्रोटोटाइपिंग, और सीमित हार्डवेयर परिवेश के लिए सबसे उपयुक्त हैं। 
  • 4B मॉडल एक स्तर ऊपर है और छोटे लोकल एजेंट या हल्के विज़ुअल रीजनिंग कार्यों के लिए बढ़िया काम करता है। 
  • 9B मॉडल लोकल मल्टीमॉडल एप्लिकेशन के लिए बेहतर है, क्योंकि यह लोकल रूप से चलाने के लिए पर्याप्त कॉम्पैक्ट है, फिर भी इतना शक्तिशाली कि गेमप्ले मैकेनिक्स का अनुमान लगा सके और उपयोगी HTML गेम कोड जनरेट कर सके। 

इस ट्यूटोरियल के लिए, हमने qwen3.5:9b मॉडल का उपयोग किया, जो क्षमता, लोकल डिप्लॉयबिलिटी, और मल्टीमॉडल रीजनिंग के बीच मजबूत संतुलन प्रदान करता है—जिससे बिना बाहरी API पर निर्भर हुए वीडियो‑फ्रेम‑टू‑ब्राउज़र‑गेम पाइपलाइन बनाना संभव हो जाता है।

आप हमारा गाइड Qwen3.5 को एक सिंगल GPU पर लोकल रूप से कैसे चलाएँ भी पढ़ सकते हैं। 

इसके लिए लोकल स्मॉल मॉडल क्यों उपयोग करें?

मैंने इस डेमो को qwen/qwen3.5-397b-a17b मॉडल के साथ भी टेस्ट किया। यह मॉडल लोकल 9B वेरिएंट की तुलना में काफी बड़ा और सामान्यतः अधिक सक्षम है, लेकिन इसे API‑आधारित इन्फरेंस की आवश्यकता पड़ती है, जिससे बाहरी निर्भरताएँ और संभावित लेटेंसी जुड़ जाती है।

व्यवहार में, यदि रनटाइम सख्त बाधा नहीं है, तो मेरे परीक्षणों में दोनों मॉडलों के बीच कुल जेनरेशन समय में लगभग ~3 मिनट का अंतर था। बड़े मॉडल ने गेमप्ले लॉजिक को कुछ हद तक स्मूथ और प्रीव्यू अनुभव को थोड़ा अधिक पॉलिश्ड बनाया, पर सुधार नाटकीय नहीं था।

ध्यान में रखते हुए कि 9B मॉडल पूरी तरह से लोकल चलता है, परिणाम आश्चर्यजनक रूप से प्रतिस्पर्धी हैं। आकार में बहुत छोटा होने के बावजूद, यह बड़े 397B मॉडल की तुलना में केवल मामूली गुणवत्ता गिरावट के साथ खेलने योग्य ब्राउज़र गेम जनरेट करने में सक्षम है।

आप 0.8B वेरिएंट से अधिकतम लाभ उठाने के लिए हमारा पूरा गाइड Qwen3.5 स्मॉल का फाइन‑ट्यूनिंग भी पढ़ सकते हैं। 

Qwen 3.5 उदाहरण प्रोजेक्ट: वीडियो‑टू‑गेम जेनेरेटर बनाएं

इस सेक्शन में, हम एक Streamlit ऐप बनाएँगे जो:

  • एक छोटा गेमप्ले क्लिप लेता है
  • कुछ प्रतिनिधि फ्रेम निकालता है
  • वे फ्रेम Ollama के जरिए qwen3.5:9b को भेजता है
  • मॉडल से एक स्ट्रक्चर्ड गेम स्पेसिफिकेशन निकालने को कहता है
  • मॉडल से सिंगल‑फाइल HTML गेम जनरेट करने को कहता है
  • Streamlit ऐप के अंदर जनरेट किए गए गेम का प्रीव्यू दिखाता है

यह सेटअप Qwen की मल्टीमॉडल क्षमताओं को उजागर करता है—जैसे गेमप्ले फुटेज से लॉजिक को रिवर्स‑इंजीनियर करना और उसे कोड में बदलना

स्टेप 1: डिपेंडेंसी इंस्टॉल करें

एप्लिकेशन बनाना शुरू करने से पहले, हमें वीडियो प्रोसेसिंग, मल्टीमॉडल इन्फरेंस और इंटरैक्टिव इंटरफेस के लिए आवश्यक लाइब्रेरी के साथ एक लोकल एन्वायरनमेंट तैयार करना होगा। 

pip install streamlit opencv-python ollama python-dotenv pydantic

डेमो में कुछ छोटी Python लाइब्रेरी उपयोग होती हैं:

  • Streamlit से वह इंटरैक्टिव वेब इंटरफेस बनता है, जहाँ उपयोगकर्ता गेमप्ले वीडियो अपलोड करते हैं और जनरेट किए गए गेम का प्रीव्यू देखते हैं।
  • OpenCV अपलोड किए गए वीडियो को डिकोड करने और प्रतिनिधि फ्रेम निकालने के लिए।
  • Ollama लोकल Qwen 3.5 मॉडल को मल्टीमॉडल रीजनिंग और कोड जेनरेशन के लिए चलाने हेतु।
  • python-dotenv जरूरत होने पर एन्वायरनमेंट वेरिएबल मैनेज करने के लिए।
  • Pydantic कोड जेनरेट करने से पहले इन्फर किए गए गेम स्पेसिफिकेशन के लिए स्ट्रक्चर्ड स्कीमा लागू करने के लिए।

इसके बाद हमें मॉडल खुद डाउनलोड करना होगा। Ollama इसे आसान बनाता है—मॉडल को सीधे अपने मॉडल रजिस्ट्री से पुल कर लें:

ollama run qwen3.5:9b

पहली बार रन पर मॉडल डाउनलोड होकर लोकल स्टोर हो जाता है। qwen3.5:9B लगभग 6.6GB का है, टेक्स्ट और इमेज इनपुट सपोर्ट करता है, और 256K टोकन कॉन्टेक्स्ट विंडो देता है, जो इसे मल्टीमॉडल वर्कफ़्लो के लिए उपयुक्त बनाता है।

स्टेप 2: इम्पोर्ट्स

अब जब लोकल एन्वायरनमेंट तैयार है, अगला कदम है एप्लिकेशन में उपयोग होने वाली लाइब्रेरी इम्पोर्ट करना और जनरेटेड आर्टिफैक्ट्स को स्टोर करने के लिए एक फ़ोल्डर बनाना। 

import json
import base64
import tempfile
from pathlib import Path
from typing import List
import cv2
import ollama
import streamlit as st
import streamlit.components.v1 as components
from pydantic import BaseModel, Field, ValidationError
OUTPUT_DIR = Path("outputs_local")
OUTPUT_DIR.mkdir(exist_ok=True)

ऊपर का ब्लॉक प्रोजेक्ट के कोर स्टैक को एक साथ लाता है। हम json का उपयोग स्ट्रक्चर्ड गेम स्पेसिफिकेशन को फ़ॉर्मेट करने के लिए, base64 का उपयोग निकाले गए वीडियो फ्रेम को मॉडल को भेजने से पहले एन्कोड करने के लिए, और tempfile का उपयोग सेशन के दौरान अपलोड किए गए वीडियो को सुरक्षित रूप से सेव करने के लिए करते हैं। pathlib की Path फ़ाइल पथ प्रबंधन का साफ़ तरीका देती है, जबकि typing का List स्कीमा डिफिनिशन में टाइप हिंट्स के लिए उपयोग होता है।

इसके बाद, हम मुख्य एप्लिकेशन लाइब्रेरी इम्पोर्ट करते हैं—OpenCV की cv2 अपलोड किए गए गेमप्ले क्लिप को पढ़ने और प्रतिनिधि फ्रेम निकालने की ज़िम्मेदारी लेती है, ollama लोकल चल रहे qwen3.5:9b मॉडल से संवाद करने का इंटरफेस है। streamlit वेब UI देता है, और streamlit.components.v1 हमें जनरेटेड HTML गेम को सीधे ऐप में एम्बेड करने देता है ताकि लाइव प्रीव्यू और इंटरेक्शन हो सके।

अंत में, हम Pydantic से BaseModel, Field, और ValidationError इम्पोर्ट करते हैं। इनका उपयोग उस स्ट्रक्चर्ड JSON स्कीमा को परिभाषित और वैलिडेट करने के लिए किया जाता है जो इन्फर किए गए गेम डिज़ाइन का प्रतिनिधित्व करता है। मॉडल से ढीले‑ढाले फ़ॉर्मेटेड टेक्स्ट पर भरोसा करने के बजाय, हम उसे कोड जेनरेटर को देने से पहले एक पूर्वानुमेय स्कीमा में बाध्य करते हैं।

आखिरी दो लाइनें अगर outputs_local डायरेक्टरी पहले से मौजूद नहीं है तो उसे बनाती हैं। इस फ़ोल्डर में हम इन्फर किया गया गेम स्पेसिफिकेशन और अंतिम HTML गेम फ़ाइल जैसे जनरेटेड आर्टिफैक्ट्स सेव करते हैं—ताकि हर रन के बाद परिणामों को देखना या डाउनलोड करना आसान हो।

अगले स्टेप में, हम वे Pydantic स्कीमा क्लास परिभाषित करेंगे जो उस गेम स्पेसिफिकेशन का वर्णन करते हैं जिसे मॉडल को निकाले गए गेमप्ले फ्रेम से जनरेट करना होगा।

स्टेप 3: स्कीमा परिभाषित करें

इस चरण में, हम वह स्ट्रक्चर्ड स्कीमा परिभाषित करते हैं जिसे मॉडल को वीडियो फ्रेम से इन्फर किए गए गेमप्ले मैकेनिक्स का वर्णन करते समय फॉलो करना होगा। 

इमेज से सीधे HTML कोड जनरेट करने को कहने के बजाय, हम पहले उससे स्ट्रक्चर्ड गेम स्पेसिफिकेशन बनवाते हैं। यह इंटरमीडिएट प्रतिनिधित्व पाइपलाइन को डिबग, वैलिडेट और एक्सटेंड करना बहुत आसान बना देता है।

class Entity(BaseModel):
    name: str
    role: str
    behavior: str
class Physics(BaseModel):
    gravity: str = ""
    jump_or_impulse: str = ""
    collision_style: str = ""
    movement_style: str = ""
class VisualStyle(BaseModel):
    perspective: str = ""
    palette: str = ""
    background: str = ""
    ui_elements: List[str] = Field(default_factory=list)
class GameSpec(BaseModel):
    title: str
    genre: str
    objective: str
    player_controls: List[str]
    gameplay_loop: List[str]
    entities: List[Entity]
    scoring_rules: List[str]
    win_condition: str
    lose_condition: str
    physics: Physics
    visual_style: VisualStyle
    assumptions: List[str]
    confidence_notes: List[str]

यह स्कीमा उस गेम स्पेसिफिकेशन की मूल संरचना परिभाषित करता है जिसे मॉडल को जनरेट करना होगा।

  • Entity क्लास गेम में मौजूद किसी भी ऑब्जेक्ट का प्रतिनिधित्व करता है, जैसे प्लेयर, दुश्मन, बाधाएँ, या UI एलीमेंट्स। हर एंटिटी में नाम, गेम में उसकी भूमिका, और उसके व्यवहार का संक्षिप्त विवरण शामिल होता है।
  • Physics क्लास बताता है कि गेम वर्ल्ड में ऑब्जेक्ट कैसे चलते और इंटरैक्ट करते हैं। इन फ़ील्ड्स में ग्रेविटी, इम्पल्स/जंप, कोलिजन हैंडलिंग और मूवमेंट स्टाइल जैसी बुनियादी मैकेनिक्स कैप्चर होते हैं।
  • VisualStyle क्लास गेम की दृश्य प्रस्तुति को कैप्चर करता है—जैसे कैमरा परिप्रेक्ष्य (टॉप‑डाउन या साइड‑स्क्रॉलिंग), कलर पैलेट, बैकग्राउंड स्टाइल, और स्कोर काउंटर या हेल्थ इंडिकेटर जैसे दिखने वाले UI एलीमेंट्स।
  • अंततः, GameSpec क्लास सबकुछ एक साथ लाता है। यह वीडियो फ्रेम से इन्फर किया गया पूरा गेम डिज़ाइन दर्शाता है—उद्देश्य, कंट्रोल्स, गेमप्ले लूप, एंटिटीज़, स्कोरिंग सिस्टम, जीत/हार की शर्तें, और वे मान्यताएँ जो मॉडल ने फ्रेम्स की व्याख्या करते समय बनाईं।

मॉडल के आउटपुट को इस स्कीमा के विरुद्ध वैलिडेट करके, हम सुनिश्चित करते हैं कि इन्फर किया गया डिज़ाइन हमेशा एक पूर्वानुमेय संरचना का पालन करे। इससे स्पेसिफिकेशन को पाइपलाइन के अगले चरण में पास करना भी बहुत आसान हो जाता है।

स्टेप 4: दो‑चरणीय जेनरेशन के लिए प्रॉम्प्ट्स

स्कीमा तय होने के बाद, अगला कदम उन प्रॉम्प्ट्स को परिभाषित करना है जो मॉडल को हमारी पाइपलाइन के दो चरणों से गुजारते हैं। 

पहला चरण निकाले गए फ्रेम से गेमप्ले मैकेनिक्स को रिवर्स‑इंजीनियर करने और एक स्ट्रक्चर्ड गेम स्पेसिफिकेशन बनाने पर केंद्रित है। 

दूसरा चरण उस स्पेसिफिकेशन को लेकर HTML, CSS, और JavaScript का उपयोग करके एक खेलने योग्य ब्राउज़र गेम बनाता है।

स्टेप 4.1: सिस्टम प्रॉम्प्ट्स परिभाषित करें

हमारी प्रॉम्प्टिंग रणनीति का पहला घटक सिस्टम प्रॉम्प्ट्स हैं, जो यह परिभाषित करते हैं कि पाइपलाइन के प्रत्येक चरण में मॉडल को किस भूमिका में कार्य करना चाहिए।

SPEC_SYSTEM_PROMPT = """
You are an expert arcade game designer and gameplay reverse-engineering analyst.
You observe a few frames from a gameplay video and infer the smallest possible playable browser-game clone.
The gameplay is likely a simple arcade game like Pong, Breakout, Snake, or Flappy Bird.
Prefer these mechanics if uncertain.
Rules:
- Only infer mechanics that are strongly supported by visual evidence.
- Prefer a minimal playable prototype over a complex clone.
- Do not invent advanced systems unless clearly visible.
- Keep the result suitable for plain HTML5 canvas and JavaScript.
- Return strict JSON only.
- No markdown.
- No code fences.
""".strip()
CODE_SYSTEM_PROMPT = """
You are a senior JavaScript game developer.
Generate a complete single-file HTML game.
Requirements:
- Return raw HTML only.
- No markdown.
- No code fences.
- Use plain HTML, CSS, and JavaScript only.
- Use HTML5 canvas unless there is a strong reason not to.
- Keep the game simple, playable, and self-contained.
- No external libraries, no CDNs, no external assets.
- Use simple shapes/colors instead of images.
- Include score.
- Include restart support.
- Include instructions on screen.
- Keep keyboard controls simple.
- Make the game playable inside an iframe preview.
- IMPORTANT: the canvas must have tabindex="0".
- IMPORTANT: keyboard input must work inside an embedded iframe.
- IMPORTANT: focus the canvas automatically on load, on canvas click, and after pressing Start/Restart buttons.
- IMPORTANT: prevent default browser behavior for arrow keys and spacebar.
""".strip()

पहला सिस्टम प्रॉम्प्ट मॉडल को गेमप्ले विश्लेषक की तरह व्यवहार करने का निर्देश देता है। यह न्यूनतम मान्यताओं पर जोर देता है और मॉडल को केवल उन्हीं मैकेनिक्स का अनुमान लगाने के लिए प्रोत्साहित करता है जो फ्रेम से मजबूत प्रमाणित हों। 

चूंकि मॉडल कुछ ही सैंपल्ड इमेज देखता है, प्रॉम्प्ट इसे स्पष्ट रूप से सरल आर्केड‑स्टाइल गेम्स की ओर पक्षपाती करता है, जिन्हें हल्की ब्राउज़र लॉजिक से लागू किया जा सके।

दूसरे सिस्टम प्रॉम्प्ट में मॉडल की भूमिका एक JavaScript डेवलपर की हो जाती है। इस चरण में, मॉडल को एक स्ट्रक्चर्ड गेम स्पेसिफिकेशन मिलता है और उसे उसे एक पूर्ण HTML गेम में बदलना होता है। 

स्टेप 4.2: गेम‑स्पेक प्रॉम्प्ट बनाएं

अगले चरण में, हम एक हेल्पर फ़ंक्शन परिभाषित करते हैं जो पाइपलाइन के पहले चरण के लिए यूज़र प्रॉम्प्ट बनाता है। यह प्रॉम्प्ट मॉडल से निकाले गए फ्रेम का विश्लेषण करने और एक स्ट्रक्चर्ड गेम स्पेसिफिकेशन बनाने को कहता है।

def build_spec_user_prompt(game_hint: str, extra_constraints: str, max_frames: int) -> str:
    return f"""
You are given a few still frames extracted from a gameplay video.
From these frames, infer a minimal, playable browser game design.
Use very simple 2D arcade-style mechanics.
If you are unsure, bias toward Pong / Breakout / Snake / Flappy Bird style games.
Output valid JSON with exactly this schema:
{{
  "title": "string",
  "genre": "string",
  "objective": "string",
  "player_controls": ["string"],
  "gameplay_loop": ["string"],
  "entities": [
    {{
      "name": "string",
      "role": "player|enemy|obstacle|projectile|ui|environment",
      "behavior": "string"
    }}
  ],
  "scoring_rules": ["string"],
  "win_condition": "string",
  "lose_condition": "string",
  "physics": {{
    "gravity": "string",
    "jump_or_impulse": "string",
    "collision_style": "string",
    "movement_style": "string"
  }},
  "visual_style": {{
    "perspective": "string",
    "palette": "string",
    "background": "string",
    "ui_elements": ["string"]
  }},
  "assumptions": ["string"],
  "confidence_notes": ["string"]
}}
Important:
- Only infer the simplest mechanics needed for a playable clone.
- Prefer a very simple game that can be rebuilt in one HTML file.
- Avoid menus, accounts, audio, networking, cutscenes, or multi-level progression.
Optional game hint from user:
{game_hint or "None"}
Extra constraints:
{extra_constraints or "None"}
You are seeing at most {max_frames} frames from the video, so stay simple and conservative.
Return JSON only.
""".strip()

यह फ़ंक्शन उस प्रॉम्प्ट का निर्माण करता है जिसे निकाले गए वीडियो फ्रेम के साथ मॉडल को भेजा जाएगा। इसमें कई महत्वपूर्ण तत्व शामिल हैं:

  • यह याद दिलाना कि मॉडल पूरे वीडियो के बजाय स्टिल फ्रेम का विश्लेषण कर रहा है
  • सरल आर्केड मैकेनिक्स की ओर बायस
  • पूरा JSON स्कीमा स्ट्रक्चर, जिससे आउटपुट हमारे GameSpec मॉडल से मेल खाए
  • वैकल्पिक रूप से, उपयोगकर्ता संकेत और बाधाएँ जोड़ सकता/सकती है ताकि इंटरफेस मॉडल को किसी खास गेम शैली की ओर निर्देशित कर सके

स्कीमा को सीधे प्रॉम्प्ट में एम्बेड करके, हम मॉडल को ऐसा आउटपुट देने के लिए प्रेरित करते हैं जिसे पाइपलाइन के अगले चरण में Pydantic द्वारा वैलिडेट किया जा सके।

स्टेप 4.3: कोड‑जेनरेशन प्रॉम्प्ट बनाएं

जब गेम स्पेसिफिकेशन जनरेट और वैलिडेट हो जाए, तो हमें उसे एक वास्तविक खेलने योग्य गेम में बदलने के लिए दूसरा प्रॉम्प्ट चाहिए।

def build_code_user_prompt(game_spec: dict) -> str:
    spec_json = json.dumps(game_spec, indent=2)
    return f"""
Using the following game specification, generate a complete single-file HTML game.
Requirements:
- One self-contained HTML file.
- Inline CSS and inline JavaScript.
- Must be playable in a browser.
- Must render correctly in an iframe.
- Show score on screen.
- Show controls on screen.
- Add restart support using a button or key.
- Keep visuals simple and robust.
- Use requestAnimationFrame.
- Use canvas for gameplay rendering.
- No external assets.
- Make the game small but genuinely playable.
Game specification:
{spec_json}
Return raw HTML only.
""".strip()

build_code_user_prompt फ़ंक्शन GameSpec डिक्शनरी को फ़ॉर्मेटेड JSON में बदलकर प्रॉम्प्ट में सम्मिलित करता है। मॉडल फिर इसी स्ट्रक्चर्ड विवरण का उपयोग डिजाइन ब्रीफ़ की तरह करके अंतिम HTML गेम जनरेट करता है।

क्योंकि स्पेसिफिकेशन पहले ही मैकेनिक्स, एंटिटीज़, कंट्रोल्स और उद्देश्य परिभाषित कर चुका है, मॉडल का कार्य सरल हो जाता है—उसे बस उस डिज़ाइन को JavaScript गेम लॉजिक और कैनवास‑आधारित रेंडरिंग लूप में अनुवाद करना होता है।

अगले चरण में, हम OpenCV का उपयोग करके फ्रेम एक्सट्रैक्शन पाइपलाइन लागू करेंगे, जो अपलोड किए गए गेमप्ले वीडियो को कुछ प्रतिनिधि इमेज में बदलता है जिन्हें Qwen 3.5 मॉडल विश्लेषित कर सकता है।

स्टेप 5: प्रतिनिधि फ्रेम निकालें

Ollama के Qwen 3.5 लोकल मॉडल टेक्स्ट और इमेज स्वीकार करते हैं, रॉ वीडियो नहीं। इसलिए अपलोड किए गए क्लिप को सीधे भेजने की जगह, हम समान अंतराल पर कुछ फ्रेम निकालते हैं और उन्हें मॉडल को पास करते हैं। 

def save_uploaded_video(uploaded_file) -> Path:
    suffix = Path(uploaded_file.name).suffix or ".mp4"
    with tempfile.NamedTemporaryFile(delete=False, suffix=suffix) as tmp:
        tmp.write(uploaded_file.read())
        return Path(tmp.name)
def extract_frames(video_path: Path, max_frames: int = 6):
    cap = cv2.VideoCapture(str(video_path))
    total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
    interval = max(1, total_frames // max_frames) if total_frames > 0 else 1
    frames = []
    frame_id = 0
    while True:
        ret, frame = cap.read()
        if not ret:
            break
        if frame_id % interval == 0:
            _, buffer = cv2.imencode(".jpg", frame)
            frames.append(base64.b64encode(buffer).decode())
        frame_id += 1
        if len(frames) >= max_frames:
            break
    cap.release()
    return frames
def extract_json_block(text: str) -> str:
    start = text.find("{")
    end = text.rfind("}")
    if start == -1 or end == -1 or end <= start:
        raise ValueError("No valid JSON object found in model response.")
    return text[start : end + 1]
def cleanup_html_response(text: str) -> str:
    html = text.strip()
    if html.startswith("```"):
        lines = html.splitlines()
        if lines and lines[0].startswith("```"):
            lines = lines[1:]
        if lines and lines[-1].startswith("```"):
            lines = lines[:-1]
        html = "\n".join(lines).strip()
    return html
def patch_html_for_iframe_keyboard(html: str) -> str:
    if 'tabindex="0"' not in html and "<canvas" in html:
        html = html.replace("<canvas", '<canvas tabindex="0"', 1)
    focus_patch = """
<script>
(function() {
  const canvas = document.querySelector("canvas");
  if (!canvas) return;
  if (!canvas.hasAttribute("tabindex")) {
    canvas.setAttribute("tabindex", "0");
  }
  function focusGame() {
    try { canvas.focus(); } catch (e) {}
  }
  window.addEventListener("load", focusGame);
  canvas.addEventListener("click", focusGame);
  document.addEventListener("keydown", function(e) {
    if (["ArrowUp","ArrowDown","ArrowLeft","ArrowRight"," "].includes(e.key)) {
      e.preventDefault();
    }
  }, { passive: false });
  const buttons = document.querySelectorAll("button");
  buttons.forEach(btn => {
    btn.addEventListener("click", () => {
      setTimeout(focusGame, 50);
    });
  });
})();
</script>
"""
    if "</body>" in html:
        html = html.replace("</body>", focus_patch + "\n</body>")
    else:
        html += focus_patch
    return html

ऊपर दिए यूटिलिटी पाँच प्रमुख घटक परिभाषित करते हैं, जो अपलोड किए गए वीडियो से प्रतिनिधि फ्रेम निकालने में काम आते हैं:

  • वीडियो अपलोड हैंडलिंग: save_uploaded_video() फ़ंक्शन अपलोड किए गए गेमप्ले वीडियो को डिस्क पर एक अस्थायी फ़ाइल के रूप में सेव करता है। क्योंकि Streamlit फाइलें मेमोरी में अपलोड करता है, फ़ाइल को डिस्क पर लिखने से OpenCV फ्रेम एक्सट्रैक्शन के दौरान उसे कुशलता से पढ़ सकता है, साथ ही मूल फ़ाइल एक्सटेंशन भी सुरक्षित रहता है।
  • वीडियो से फ्रेम सैंपलिंग: extract_frames() फ़ंक्शन OpenCV के जरिए वीडियो लोड करता है और इसकी अवधि में समान अंतराल पर कुछ फ्रेम सैंपल करता है। हर फ्रेम को JPEG में एन्कोड करके base64 स्ट्रिंग में बदला जाता है ताकि उसे मल्टीमॉडल Qwen मॉडल को सीधे इमेज इनपुट के रूप में भेजा जा सके।
  • स्ट्रक्चर्ड JSON एक्सट्रैक्शन: extract_json_block() हेल्पर यह सुनिश्चित करता है कि मॉडल आउटपुट सही ढंग से पार्स हो सके। निकाला गया कंटेंट फिर सुरक्षित रूप से GameSpec स्कीमा के विरुद्ध वैलिडेट किया जा सकता है।
  • HTML रिस्पॉन्स क्लीनअप: cleanup_html_response() वह मार्कडाउन फ़ॉर्मेटिंग हटाता है जो मॉडल कोड जनरेट करते समय जोड़ सकता है। अगर मॉडल HTML आउटपुट को ट्रिपल बैकटिक कोड फेंस में लपेट दे, तो यह हेल्पर उन्हें हटा देता है ताकि HTML सीधे Streamlit प्रीव्यू में रेंडर हो सके।
  • एम्बेडेड गेम्स के लिए कीबोर्ड इंटरेक्शन: patch_html_for_iframe_keyboard() जनरेटेड HTML में एक छोटा JavaScript पैच इंजेक्ट करता है ताकि Streamlit के iframe के अंदर भी कीबोर्ड कंट्रोल सही से काम करें। यह कैनवास एलीमेंट को ऑटो‑फोकस करता है, जरूरत हो तो tabindex असाइन करता है, और ब्राउज़र को एरो कीज़ व स्पेसबार इंटरसेप्ट करने से रोकता है, ताकि वे गेम कंट्रोल के रूप में काम करें।

मिलकर, ये यूटिलिटी पाइपलाइन की वीडियो प्रीप्रोसेसिंग और रिस्पॉन्स‑हैंडलिंग लेयर बनाते हैं।

स्टेप 6: गेम स्पेसिफिकेशन इन्फर करें 

अब जब हमारे पास निकाले गए फ्रेम और प्रॉम्प्ट टेम्पलेट तैयार हैं, हम रीजनिंग पाइपलाइन का पहला चरण लागू कर सकते हैं—यानी गेमप्ले फ्रेम को स्ट्रक्चर्ड गेम स्पेसिफिकेशन में बदलना।

def infer_game_spec(video_path: Path, game_hint: str, extra_constraints: str, max_frames: int) -> dict:
    frames = extract_frames(video_path, max_frames=max_frames)
    prompt = build_spec_user_prompt(game_hint, extra_constraints, max_frames)
    response = ollama.chat(
        model="qwen3.5:9b",
        messages=[
            {
                "role": "system",
                "content": SPEC_SYSTEM_PROMPT,
            },
            {
                "role": "user",
                "content": prompt,
                "images": frames,
            },
        ],
        options={
            "temperature": 0.1,
            "num_ctx": 8192,
        },
    )
    text = response["message"]["content"]
    parsed = json.loads(extract_json_block(text))
    validated = GameSpec(**parsed)
    return validated.model_dump()

ऊपर का फ़ंक्शन गेम‑स्पेक इन्फरेंस पाइपलाइन में चार प्रमुख चरण परिभाषित करता है:

  • फ्रेम तैयारी: extract_frames() कॉल अपलोड किए गए गेमप्ले वीडियो से कुछ प्रतिनिधि फ्रेम सैंपल करता है। ये इमेज वही दृश्य साक्ष्य हैं जिनसे मॉडल गेम मैकेनिक्स का अनुमान लगाएगा।
  • प्रॉम्प्ट निर्माण: build_spec_user_prompt() फ़ंक्शन वैकल्पिक हिंट, अतिरिक्त बाधाएँ, और निकाले गए फ्रेम की अधिकतम संख्या का उपयोग करके यूज़र प्रॉम्प्ट को डायनेमिक रूप से बनाता है। यह प्रॉम्प्ट मॉडल को स्पष्ट निर्देश देता है कि स्कीमा‑अनुरूप गेम स्पेसिफिकेशन तैयार करे।
  • मल्टीमॉडल मॉडल कॉल: ollama.chat() रिक्वेस्ट टेक्स्ट प्रॉम्प्ट और निकाले गए फ्रेम दोनों को लोकल qwen3.5:9b मॉडल को भेजती है। जेनरेशन सेटिंग्स में कम temperature रखा गया है ताकि आउटपुट निर्णायक रहे, और num_ctx=8192 ताकि प्रॉम्प्ट और स्ट्रक्चर्ड रिस्पॉन्स के लिए पर्याप्त कॉन्टेक्स्ट उपलब्ध हो।
  • स्कीमा वैलिडेशन: मॉडल रिस्पॉन्स मिलने पर, फ़ंक्शन JSON ब्लॉक निकालता है, उसे पार्स करता है, और GameSpec Pydantic स्कीमा के विरुद्ध वैलिडेट करता है। अंत में, वैलिडेटेड स्पेसिफिकेशन Python डिक्शनरी के रूप में रिटर्न होता है।

अब हमारे पास उस गेम का स्ट्रक्चर्ड प्रतिनिधित्व है जिसे मॉडल ने सैंपल्ड वीडियो फ्रेम से इन्फर किया। अगले चरण में, हम इस वैलिडेटेड स्पेसिफिकेशन का उपयोग HTML गेम जेनरेशन स्टेज के इनपुट के रूप में करेंगे।

स्टेप 7: HTML गेम जनरेट करें

इस बिंदु पर, मॉडल को इमेज से मैकेनिक्स इन्फर नहीं करने हैं। इसकी जगह, हम qwen3.5:9b से एक सिंगल, सेल्फ‑कंटेन्ड HTML फ़ाइल बनाने को कहते हैं, जिसमें लेआउट, स्टाइल्स, गेम लूप, कंट्रोल्स, और रेंडरिंग लॉजिक शामिल हो।

def generate_game_html(game_spec: dict) -> str:
    response = ollama.chat(
        model="qwen3.5:9b",
        messages=[
            {
                "role": "system",
                "content": CODE_SYSTEM_PROMPT,
            },
            {
                "role": "user",
                "content": build_code_user_prompt(game_spec),
            },
        ],
        options={
            "temperature": 0.2,
            "num_ctx": 8192,
        },
    )
    text = response["message"]["content"]
    html = cleanup_html_response(text)
    if "<html" not in html.lower():
        raise ValueError("Model did not return HTML")
    return patch_html_for_iframe_keyboard(html)

ऊपर का फ़ंक्शन अंतिम HTML गेम को जनरेट और तैयार करने के लिए निम्न चार कदम उठाता है:

  • स्ट्रक्चर्ड प्रॉम्प्ट डिलिवरी: यह वैलिडेटेड game_spec डिक्शनरी को लेकर build_code_user_prompt() में पास करता है। इससे गेम स्पेसिफिकेशन एक विस्तृत इम्प्लीमेंटेशन प्रॉम्प्ट में बदल जाता है, जो मॉडल को एक संपूर्ण सिंगल‑फाइल HTML गेम जनरेट करने का निर्देश देता है।
  • Qwen मॉडल के साथ कोड जेनरेशन: ollama.chat() कॉल कोड‑जेनरेशन सिस्टम प्रॉम्प्ट और यूज़र प्रॉम्प्ट को लोकल qwen3.5:9b मॉडल को भेजती है। चूंकि अब कार्य मल्टीमॉडल रीजनिंग के बजाय शुद्ध कोड जेनरेशन है, मॉडल को केवल टेक्स्ट मिलता है। 
  • HTML क्लीनअप और वैलिडेशन: मॉडल के रॉ रिस्पॉन्स को cleanup_html_response() से गुज़ारा जाता है ताकि कोई मार्कडाउन या अतिरिक्त फ़ॉर्मेटिंग हट जाए। फिर एक साधारण जाँच करके पुष्टि की जाती है कि आउटपुट में एक HTML डॉक्यूमेंट मौजूद है।
  • Iframe कीबोर्ड पैचिंग: अंतिम HTML को patch_html_for_iframe_keyboard() से पास किया जाता है, जो एक छोटा JavaScript फिक्स इंजेक्ट करता है ताकि गेम Streamlit के एम्बेडेड iframe के अंदर खेलने योग्य रहे। यह चरण महत्वपूर्ण है क्योंकि ब्राउज़र गेम अक्सर एरो कीज़ और स्पेसबार को सही से कैप्चर नहीं कर पाते, जब तक कि कैनवास को स्पष्ट रूप से फोकस करने योग्य न बनाया जाए और उसे ऑटो‑फोकस न कराया जाए।

अब हमारे पास पूरी तरह से इन्फर किए गए गेम स्पेसिफिकेशन से जनरेट किया गया एक ब्राउज़र‑प्लेयेबल HTML गेम है।

स्टेप 8: Streamlit UI

अंतिम चरण के रूप में, आइए सबकुछ एक सरल Streamlit इंटरफेस में समेटें ताकि उपयोगकर्ता बिना अंडरलाईंग कोड छुए सिस्टम को आज़मा सकें। इंटरफेस तीन मुख्य क्षमताएँ एक्सपोज़ करता है:

  • गेम्प्ले वीडियो अपलोड करना
  • हिंट्स और फ्रेम सैंपलिंग जैसी जेनरेशन पैरामीटर्स को कॉन्फ़िगर करना
  • जनरेटेड गेम का प्रीव्यू और डाउनलोड करना
st.set_page_config(page_title="Qwen 3.5 9B (Ollama) - Video to Game", layout="wide")
st.title("Qwen 3.5 9B: Gameplay Video to Playable HTML Game")
st.write(
    "This local demo uses Ollama with qwen3.5:9b to turn a short gameplay clip "
    "into a minimal browser game."
)
with st.sidebar:
    st.header("Local generation settings")
    game_hint = st.text_input("Optional game hint", value="Simple Pong-like arcade game")
    extra_constraints = st.text_area(
        "Extra constraints",
        value="Keep the game extremely simple and easy to play with arrow keys or space bar.",
        height=120,
    )
    max_frames = st.slider("Frames extracted from video", 3, 8, 5)
uploaded_video = st.file_uploader(
    "Upload gameplay video",
    type=["mp4", "mov", "avi", "mkv", "webm"],
)
if "local_game_spec" not in st.session_state:
    st.session_state.local_game_spec = None
if "local_game_html" not in st.session_state:
    st.session_state.local_game_html = None
if uploaded_video is not None:
    video_path = save_uploaded_video(uploaded_video)
    col1, col2 = st.columns([1, 1])
    with col1:
        st.subheader("Uploaded video")
        st.video(str(video_path))
    with col2:
        st.subheader("Run local pipeline")
        if st.button("Generate game", type="primary"):
            try:
                with st.spinner("Extracting frames and inferring game mechanics (local model)..."):
                    game_spec = infer_game_spec(
                        video_path=video_path,
                        game_hint=game_hint,
                        extra_constraints=extra_constraints,
                        max_frames=max_frames,
                    )
                    st.session_state.local_game_spec = game_spec
                with st.spinner("Generating HTML game code with qwen3.5:9b..."):
                    game_html = generate_game_html(game_spec)
                    st.session_state.local_game_html = game_html
                    (OUTPUT_DIR / "local_game_spec.json").write_text(
                        json.dumps(game_spec, indent=2),
                        encoding="utf-8",
                    )
                    (OUTPUT_DIR / "local_generated_game.html").write_text(
                        game_html,
                        encoding="utf-8",
                    )
                st.success("Local game generated.")
            except ValidationError as e:
                st.error(f"Schema validation failed: {e}")
            except Exception as e:
                st.error(f"Local generation failed: {e}")
if st.session_state.local_game_spec:
    st.subheader("Inferred game spec")
    st.json(st.session_state.local_game_spec)
if st.session_state.local_game_html:
    tab1, tab2, tab3 = st.tabs(["Preview", "HTML Code", "Downloads"])
    with tab1:
        st.subheader("Playable preview")
        st.caption(
            "Click START GAME, then click once inside the game canvas so it captures keyboard input."
        )
        components.html(
            st.session_state.local_game_html,
            height=760,
            scrolling=False,
        )
    with tab2:
        st.subheader("Generated HTML (local)")
        st.code(st.session_state.local_game_html, language="html")
    with tab3:
        st.subheader("Download files (local)")
        st.download_button(
            label="Download HTML game (local)",
            data=st.session_state.local_game_html,
            file_name="local_generated_game.html",
            mime="text/html",
        )
        st.download_button(
            label="Download JSON spec (local)",
            data=json.dumps(st.session_state.local_game_spec, indent=2),
            file_name="local_game_spec.json",
            mime="application/json",
        )

उपरोक्त कोड यह परिभाषित करता है कि उपयोगकर्ता कैसे एक गेमप्ले क्लिप अपलोड करता है, वैकल्पिक रूप से गेम के प्रकार के बारे में एक संकेत देता है, और यह नियंत्रित करता है कि विश्लेषण के लिए वीडियो से कितने फ्रेम निकाले जाने चाहिए। वीडियो अपलोड होने के बाद, ऐप उसके साथ एक Generate game बटन दिखाता है, जो पूरी पाइपलाइन को ट्रिगर करता है।

जब उपयोगकर्ता पाइपलाइन चलाता है, तो ऐप पहले फ्रेम निकालता है और स्थानीय qwen3.5:9b मॉडल का उपयोग करके गेम मैकेनिक्स का अनुमान लगाता है, जिससे एक संरचित गेम विनिर्देश तैयार होता है। दूसरे चरण में, मॉडल उस विनिर्देश से एक पूर्ण HTML गेम उत्पन्न करता है। परिणाम Streamlit के सेशन स्टेट में संग्रहीत किए जाते हैं ताकि वे UI अपडेट के दौरान बने रहें।

जेनरेशन पूरा होने के बाद, इंटरफ़ेस अनुमानित गेम विनिर्देश, जेनरेट किए गए गेम का लाइव खेलने योग्य प्रीव्यू, कच्चा HTML सोर्स, और HTML गेम तथा JSON विनिर्देश—दोनों को सहेजने के लिए डाउनलोड बटन दिखाता है।

इसे खुद आज़माने के लिए, कोड को app.py के रूप में सहेजें और लॉन्च करें:

streamlit run app.py

Demo

निष्कर्ष

Qwen 3.5 इस समय उपलब्ध सबसे रोचक मल्टीमॉडल मॉडल परिवारों में से एक है। इस ट्यूटोरियल में, हमने Ollama के माध्यम से qwen3.5:9b का उपयोग करके पूरी तरह स्थानीय गेमप्ले वीडियो-से-गेम जेनरेटर बनाया। ऐप एक छोटे क्लिप से फ्रेम निकालता है, एक संरचित गेम डिज़ाइन का अनुमान लगाता है, सिंगल-फाइल ब्राउज़र गेम जेनरेट करता है, और उसे Streamlit के अंदर प्रीव्यू करता है।

होस्टेड qwen/qwen3.5-397b-a17b मॉडल अभी भी इस काम के लिए तेज़ और अधिक सक्षम विकल्प है, लेकिन स्थानीय 9B का परिणाम अपने आप में प्रभावशाली है। यह सिद्ध करता है कि अब एक छोटा मल्टीमॉडल मॉडल उपभोक्ता हार्डवेयर पर भी सार्थक विज़ुअल रीजनिंग और कोड जेनरेशन कर सकता है।

यदि आप इसे आगे बढ़ाना चाहें, तो 9B की तुलना 4B और 27B मॉडलों से करें, एक रिफाइनमेंट लूप जोड़ें, या आउटपुट को एक मिनी गेम इंजन में बदलें जो उसी क्लिप से कई पुनरावृत्तियों का समर्थन करता हो।

Qwen3.5 Small FAQs

Can I run this demo with `qwen3.5:4b` instead of 9B?

हाँ, लेकिन परिणाम आम तौर पर कम भरोसेमंद होंगे। 4B मॉडल हल्के स्थानीय प्रयोगों के लिए उपयोगी है, पर 9B वैरिएंट गति और गुणवत्ता के बीच बेहतर संतुलन देता है। Ollama दोनों को स्थानीय टेक्स्ट-एंड-इमेज मॉडलों के रूप में एक्सपोज़ करता है।

Why not use `qwen3.5:0.8b` or 2B?

2B मॉडल इस पूरी पाइपलाइन की बजाय सरल मल्टीमॉडल कार्यों या एज-स्टाइल डिप्लॉयमेंट्स के लिए अधिक उपयुक्त हैं।

What is the best local Qwen 3.5 model for this use case?

सबसे अच्छा आरंभिक बिंदु qwen3.5:9b है। यह Ollama में 6.6GB पर अभी भी कॉम्पैक्ट है, टेक्स्ट-एंड-इमेज इनपुट सपोर्ट करता है, और इस तरह के रिसनिंग-हेवी जेनरेशन कार्य के लिए बहुत छोटे वैरिएंट्स से काफी बेहतर प्रदर्शन करता है।

Can I use the larger Qwen 3.5 models locally, too?

हाँ। Ollama वर्तमान में 27b, 35b और 122b वैरिएंट भी सूचीबद्ध करता है, सभी टेक्स्ट-एंड-इमेज सपोर्ट के साथ। समझौता मेमोरी और लेटेंसी का है।


Aashi Dutt's photo
Author
Aashi Dutt
LinkedIn
Twitter

मैं एमएल (Gen AI) में Google Developers Expert, Kaggle 3x Expert, और Women Techmakers एंबेसडर हूँ, तथा तकनीक क्षेत्र में 3+ वर्षों का अनुभव रखती हूँ। मैंने 2020 में एक हेल्थ-टेक स्टार्टअप की सह-स्थापना की और वर्तमान में Georgia Tech से कंप्यूटर विज्ञान में मास्टर कर रही हूँ, जिसमें मेरा विशेषज्ञता क्षेत्र मशीन लर्निंग है।

विषय
कृत्रिम बुद्धिमत्ता
बड़े भाषा मॉडल

Top DataCamp Courses

{count, plural, one {कोर्स} other {कोर्स}}

Cursor के साथ सॉफ्टवेयर डेवलपमेंट

{hours} घंटा {minutes} मिनट
4.3K
Cursor के साथ प्रोडक्शन-रेडी कोड बनाएं। AI प्रॉम्प्ट, रिफैक्टरिंग, टेस्टिंग और उन्नत वर्कफ़्लो सीखें।
विवरण देखेंRight Arrow
पाठ्यक्रम शुरू करें

{count, plural, one {कोर्स} other {कोर्स}}

डेवलपर्स के लिए AI-असिस्टेड कोडिंग

{hours} घंटा {minutes} मिनट
11K
AI से अपनी कोडिंग को बेहतर बनाएं—अपने कोडिंग असिस्टेंट को कोड लिखने, टेस्ट करने और दस्तावेज़ीकरण करने के लिए प्रभावी ढंग से मार्गदर्शन करें।

{count, plural, one {कोर्स} other {कोर्स}}

Hugging Face smolagents के साथ AI Agents

{hours} घंटा
2.8K
Python का उपयोग करके बुद्धिमान एजेंट बनाना सीखें जो तर्क करें, कार्य करें और वास्तविक दुनिया के कार्य हल करें।
और देखेंRight Arrow