Kurs vibe coding z AI · Moduł 10: TikTok Clone

AI Image Generation - GPT Image/Stable Diffusion

1 min czytania

Generowanie obrazków przez AI.

OpenAI GPT Image

Modele DALL-E zostały wyłączone w API OpenAI 12 maja 2026. Obecne modele obrazów, np. gpt-image-2, zwracają obraz jako base64 (pole b64_json), a nie jako adres URL.

npm install openai
import OpenAI from 'openai';

const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

async function generateImage(prompt: string) {
  const response = await openai.images.generate({
    model: "gpt-image-2",
    prompt,
    n: 1,
    size: "1024x1024"
  });

  return `data:image/png;base64,${response.data[0].b64_json}`;
}

function ImageGenerator() {
  const [prompt, setPrompt] = useState("");
  const [imageUrl, setImageUrl] = useState("");
  const [loading, setLoading] = useState(false);

  const handleGenerate = async () => {
    setLoading(true);
    const url = await generateImage(prompt);
    setImageUrl(url);
    setLoading(false);
  };

  return (
    <div>
      <textarea
        value={prompt}
        onChange={(e) => setPrompt(e.target.value)}
        placeholder="Opisz obraz, który chcesz wygenerować..."
        className="w-full border rounded p-3"
      />
      <button
        onClick={handleGenerate}
        disabled={loading}
        className="bg-blue-600 text-white px-4 py-2 rounded"
      >
        {loading ? 'Generating...' : 'Generate Image'}
      </button>
      {imageUrl && <img src={imageUrl} alt="Generated" className="mt-4" />}
    </div>
  );
}

Do zobaczenia!

Kod do tej lekcji: App.jsx
// Nagrywanie wideo
import { useState, useEffect } from 'react';

export default function App() {
  const [isRecording, setIsRecording] = useState(false);
  const [duration, setDuration] = useState(0);
  const maxDuration = 60;

  useEffect(() => {
    let interval;
    if (isRecording) {
      interval = setInterval(() => {
        setDuration(d => {
          if (d >= maxDuration) {
            setIsRecording(false);
            return d;
          }
          return d + 0.1;
        });
      }, 100);
    } else {
      setDuration(0);
    }
    return () => clearInterval(interval);
  }, [isRecording]);

  return (
    <div className="h-screen bg-black flex flex-col">
      {/* Podgląd wideo */}
      <div className="flex-1 relative bg-gradient-to-br from-purple-900 to-blue-900 flex items-center justify-center">
        <div className="text-9xl">◆</div>

        {/* Wskaźnik nagrywania */}
        {isRecording && (
          <div className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2">
            <div className="w-20 h-20 border-4 border-red-500 rounded-full animate-pulse"></div>
          </div>
        )}

        {/* Wskaźnik dźwięku */}
        <div className="absolute top-4 right-4 bg-black/50 px-3 py-2 rounded-full text-white text-sm flex items-center gap-2">
          Hymn piratów
        </div>
      </div>

      {/* Sterowanie */}
      <div className="bg-black border-t border-gray-800 p-6">
        {/* Pasek długości nagrania */}
        <div className="mb-6">
          <div className="h-1 bg-gray-700 rounded-full overflow-hidden">
            <div
              className={`h-full transition-all ${isRecording ? 'bg-red-500' : 'bg-gray-500'}`}
              style={{ width: `${(duration / maxDuration) * 100}%` }}
            />
          </div>
          <div className="flex justify-between mt-2 text-xs text-gray-400">
            <span>{duration.toFixed(1)}s</span>
            <span>{maxDuration}s</span>
          </div>
        </div>

        {/* Akcje */}
        <div className="flex items-center justify-around">
          {/* Efekty */}
          <button className="flex flex-col items-center gap-1">
            <div className="w-12 h-12 bg-gray-800 rounded-full flex items-center justify-center text-2xl">
              ◆
            </div>
            <span className="text-xs text-white">Efekty</span>
          </button>

          {/* Przycisk nagrywania */}
          <button
            onClick={() => setIsRecording(!isRecording)}
            className={`w-20 h-20 rounded-full border-4 flex items-center justify-center transition ${
              isRecording ? 'border-red-500 bg-red-500' : 'border-white bg-transparent'
            }`}
          >
            {isRecording && <div className="w-8 h-8 bg-white rounded"></div>}
          </button>

          {/* Dźwięki */}
          <button className="flex flex-col items-center gap-1">
            <div className="w-12 h-12 bg-gray-800 rounded-full flex items-center justify-center text-2xl">
              ◉
            </div>
            <span className="text-xs text-white">Dźwięki</span>
          </button>
        </div>
      </div>
    </div>
  );
}

Widzisz błąd w tej lekcji?

Sprawdź się

Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.

  1. 1. Do czego służy model OpenAI gpt-image-2 według lekcji?

  2. 2. Której nazwy modelu używa kod generowania obrazu?

Zadania praktyczne w grze

  • Układanie w poziomie

    Ułóż początek wywołania generowania obrazu przez OpenAI:

Przydatne artykuły