import pygame, time, threading, base64, os from flask import Flask, render_template_string from flask_socketio import SocketIO, emit from io import BytesIO from PIL import Image os.environ['SDL_VIDEODRIVER'] = 'dummy' pygame.init() WIDTH, HEIGHT = 400, 300 # Smaller for faster transfer app = Flask(__name__) app.config['SECRET_KEY'] = 'secret!' socketio = SocketIO(app, async_mode='threading') # Shared state shared = {"frame_count": 0, "streaming": True} def game_loop(): """Game loop that emits frames via WebSocket""" screen = pygame.Surface((WIDTH, HEIGHT)) circle_x, circle_y = WIDTH//2, HEIGHT//2 speed_x, speed_y = 3, 2 while shared["streaming"]: start = time.time() # Update position circle_x += speed_x circle_y += speed_y if circle_x < 30 or circle_x > WIDTH-30: speed_x *= -1 if circle_y < 30 or circle_y > HEIGHT-30: speed_y *= -1 # Draw screen.fill((25, 25, 45)) pygame.draw.circle(screen, (255, 80, 80), (int(circle_x), int(circle_y)), 20) # Convert to JPEG (small, efficient) frame_str = pygame.image.tostring(screen, 'RGB') img = Image.frombytes('RGB', (WIDTH, HEIGHT), frame_str) buf = BytesIO() img.save(buf, format='JPEG', quality=85) # Smaller file frame_b64 = base64.b64encode(buf.getvalue()).decode('utf-8') # Emit to ALL connected clients socketio.emit('new_frame', { 'frame': frame_b64, 'count': shared["frame_count"] }) shared["frame_count"] += 1 # Control FPS elapsed = time.time() - start if elapsed < 1.0/30: time.sleep(1.0/30 - elapsed) HTML = '''
Frames: 0
''' @app.route('/') def index(): return render_template_string(HTML) if __name__ == '__main__': # Start game loop in background threading.Thread(target=game_loop, daemon=True).start() # Start server socketio.run(app, host='0.0.0.0', port=int(os.environ.get('PORT', 7860)), debug=False, allow_unsafe_werkzeug=True)