We use cookies to enhance your experience on the site
CodeWorlds

Sounds Library and Video Creation

TikTok's power lies in sounds - audio tracks used by millions of videos. Plus advanced video creation with effects!

Sounds Library

1interface Sound {
2  id: string;
3  name: string;
4  artist: string;
5  duration: number;
6  thumbnailUrl: string;
7
8  // Stats
9  useCount: number; // how many videos use this sound
10  favorites: number;
11  isTrending: boolean;
12
13  // Categories
14  category: 'original' | 'music' | 'trending' | 'effects';
15  tags: string[];
16
17  createdAt: Date;
18}
19
20interface SoundUsage {
21  soundId: string;
22  startTime: number; // which second of sound to start (0-based)
23  volume: number; // 0-1
24}

Sounds Discovery Page

1'use client';
2
3import { useState } from 'react';
4
5interface SoundsPageProps {
6  sounds: Sound[];
7  onUseSound: (soundId: string) => void;
8  onFavorite: (soundId: string) => void;
9}
10
11export default function SoundsPage({
12  sounds,
13  onUseSound,
14  onFavorite
15}: SoundsPageProps) {
16  const [activeTab, setActiveTab] = useState<'trending' | 'favorites' | 'discover'>('trending');
17  const [searchQuery, setSearchQuery] = useState('');
18
19  const filteredSounds = sounds.filter(sound => {
20    if (searchQuery) {
21      return sound.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
22             sound.artist.toLowerCase().includes(searchQuery.toLowerCase());
23    }
24
25    if (activeTab === 'trending') return sound.isTrending;
26    if (activeTab === 'favorites') return sound.favorites > 1000;
27    return true;
28  });
29
30  return (
31    <div className="min-h-screen bg-black text-white">
32      {/* Header */}
33      <div className="sticky top-0 bg-black z-10 border-b border-gray-800">
34        <div className="p-4">
35          <h1 className="text-2xl font-bold mb-4">🎵 Sounds</h1>
36
37          {/* Search */}
38          <input
39            type="text"
40            value={searchQuery}
41            onChange={(e) => setSearchQuery(e.target.value)}
42            placeholder="Search sounds..."
43            className="w-full bg-gray-900 text-white px-4 py-3 rounded-lg outline-none"
44          />
45        </div>
46
47        {/* Tabs */}
48        <div className="flex border-b border-gray-800">
49          {['trending', 'favorites', 'discover'].map(tab => (
50            <button
51              key={tab}
52              onClick={() => setActiveTab(tab as any)}
53              className={`flex-1 py-3 text-sm font-semibold capitalize ${
54                activeTab === tab
55                  ? 'border-b-2 border-white'
56                  : 'text-gray-500'
57              }`}
58            >
59              {tab}
60            </button>
61          ))}
62        </div>
63      </div>
64
65      {/* Sounds List */}
66      <div className="p-4 space-y-3">
67        {filteredSounds.map(sound => (
68          <SoundCard
69            key={sound.id}
70            sound={sound}
71            onUse={() => onUseSound(sound.id)}
72            onFavorite={() => onFavorite(sound.id)}
73          />
74        ))}
75      </div>
76    </div>
77  );
78}
79
80function SoundCard({
81  sound,
82  onUse,
83  onFavorite
84}: {
85  sound: Sound;
86  onUse: () => void;
87  onFavorite: () => void;
88}) {
89  const [isPlaying, setIsPlaying] = useState(false);
90
91  return (
92    <div className="bg-gray-900 rounded-lg p-4 flex items-center gap-4">
93      {/* Thumbnail */}
94      <button
95        onClick={() => setIsPlaying(!isPlaying)}
96        className="relative w-16 h-16 rounded-lg overflow-hidden flex-shrink-0"
97      >
98        <img
99          src={sound.thumbnailUrl}
100          alt={sound.name}
101          className={`w-full h-full object-cover ${isPlaying ? 'animate-pulse' : ''}`}
102        />
103        <div className="absolute inset-0 flex items-center justify-center bg-black bg-opacity-30">
104          <span className="text-white text-2xl">
105            {isPlaying ? '⏸' : '▶'}
106          </span>
107        </div>
108      </button>
109
110      {/* Info */}
111      <div className="flex-1 min-w-0">
112        <h3 className="font-bold text-white truncate">{sound.name}</h3>
113        <p className="text-sm text-gray-400 truncate">{sound.artist}</p>
114
115        <div className="flex items-center gap-3 mt-2 text-xs text-gray-500">
116          <span>{formatCount(sound.useCount)} videos</span>
117          {sound.isTrending && (
118            <span className="flex items-center gap-1 text-pink-500">
119              🔥 Trending
120            </span>
121          )}
122        </div>
123      </div>
124
125      {/* Actions */}
126      <div className="flex flex-col gap-2">
127        <button
128          onClick={onUse}
129          className="px-4 py-2 bg-white text-black rounded-full text-sm font-semibold hover:bg-gray-200"
130        >
131          Use Sound
132        </button>
133        <button
134          onClick={onFavorite}
135          className="p-2 hover:bg-gray-800 rounded-full"
136        >
137138        </button>
139      </div>
140    </div>
141  );
142}
143
144function formatCount(count: number): string {
145  if (count >= 1000000) return `${(count / 1000000).toFixed(1)}M`;
146  if (count >= 1000) return `${(count / 1000).toFixed(1)}K`;
147  return count.toString();
148}

Video Creation Flow

1interface VideoCreationState {
2  // Recording
3  recordedChunks: Blob[];
4  isRecording: boolean;
5  recordingDuration: number;
6  maxDuration: number; // 15, 60, or 180 seconds
7
8  // Editing
9  selectedSound?: Sound;
10  soundVolume: number;
11  selectedEffect?: Effect;
12  filters: Filter[];
13  trimStart: number;
14  trimEnd: number;
15
16  // Caption
17  caption: string;
18  hashtags: string[];
19  mentions: string[];
20
21  // Privacy
22  privacy: 'public' | 'friends' | 'private';
23  allowComments: boolean;
24  allowDuet: boolean;
25  allowStitch: boolean;
26}
27
28interface Effect {
29  id: string;
30  name: string;
31  type: 'beauty' | 'ar' | 'green-screen' | 'time';
32  thumbnailUrl: string;
33  isPopular: boolean;
34}
35
36interface Filter {
37  id: string;
38  name: string;
39  intensity: number; // 0-1
40}

Video Recorder

1'use client';
2
3import { useState, useRef, useEffect } from 'react';
4
5export default function VideoRecorder({
6  maxDuration = 60,
7  onComplete: (videoBlob: Blob, metadata: any) => void
8}: {
9  maxDuration?: number;
10  onComplete: (videoBlob: Blob, metadata: any) => void;
11}) {
12  const [isRecording, setIsRecording] = useState(false);
13  const [duration, setDuration] = useState(0);
14  const [selectedSound, setSelectedSound] = useState<Sound | null>(null);
15  const [selectedEffect, setSelectedEffect] = useState<Effect | null>(null);
16
17  const videoRef = useRef<HTMLVideoElement>(null);
18  const mediaRecorderRef = useRef<MediaRecorder | null>(null);
19  const chunksRef = useRef<Blob[]>([]);
20
21  useEffect(() => {
22    // Initialize camera
23    navigator.mediaDevices.getUserMedia({ video: true, audio: true })
24      .then(stream => {
25        if (videoRef.current) {
26          videoRef.current.srcObject = stream;
27        }
28      });
29  }, []);
30
31  const startRecording = () => {
32    if (!videoRef.current?.srcObject) return;
33
34    const stream = videoRef.current.srcObject as MediaStream;
35    const mediaRecorder = new MediaRecorder(stream);
36
37    mediaRecorder.ondataavailable = (e) => {
38      if (e.data.size > 0) {
39        chunksRef.current.push(e.data);
40      }
41    };
42
43    mediaRecorder.onstop = () => {
44      const blob = new Blob(chunksRef.current, { type: 'video/webm' });
45      onComplete(blob, {
46        duration,
47        sound: selectedSound,
48        effect: selectedEffect
49      });
50    };
51
52    chunksRef.current = [];
53    mediaRecorder.start();
54    mediaRecorderRef.current = mediaRecorder;
55    setIsRecording(true);
56
57    // Duration counter
58    const interval = setInterval(() => {
59      setDuration(d => {
60        if (d >= maxDuration) {
61          stopRecording();
62          clearInterval(interval);
63          return d;
64        }
65        return d + 0.1;
66      });
67    }, 100);
68  };
69
70  const stopRecording = () => {
71    if (mediaRecorderRef.current && isRecording) {
72      mediaRecorderRef.current.stop();
73      setIsRecording(false);
74      setDuration(0);
75    }
76  };
77
78  return (
79    <div className="relative h-screen bg-black flex flex-col">
80      {/* Video Preview */}
81      <div className="flex-1 relative">
82        <video
83          ref={videoRef}
84          autoPlay
85          playsInline
86          muted
87          className="w-full h-full object-cover"
88        />
89
90        {/* Effect Overlay */}
91        {selectedEffect && (
92          <div className="absolute top-4 left-4 bg-black bg-opacity-50 px-3 py-2 rounded-full text-white text-sm">
93{selectedEffect.name}
94          </div>
95        )}
96
97        {/* Sound Indicator */}
98        {selectedSound && (
99          <div className="absolute top-4 right-4 bg-black bg-opacity-50 px-3 py-2 rounded-full text-white text-sm flex items-center gap-2">
100            🎵 {selectedSound.name}
101          </div>
102        )}
103
104        {/* Recording Indicator */}
105        {isRecording && (
106          <div className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2">
107            <div className="w-20 h-20 border-4 border-red-500 rounded-full animate-pulse"></div>
108          </div>
109        )}
110      </div>
111
112      {/* Controls */}
113      <div className="bg-black border-t border-gray-800 p-6">
114        {/* Duration Bar */}
115        <div className="mb-6">
116          <div className="h-1 bg-gray-700 rounded-full overflow-hidden">
117            <div
118              className={`h-full transition-all ${
119                isRecording ? 'bg-red-500' : 'bg-gray-500'
120              }`}
121              style={{ width: `${(duration / maxDuration) * 100}%` }}
122            />
123          </div>
124          <div className="flex justify-between mt-2 text-xs text-gray-400">
125            <span>{duration.toFixed(1)}s</span>
126            <span>{maxDuration}s</span>
127          </div>
128        </div>
129
130        {/* Actions */}
131        <div className="flex items-center justify-around">
132          {/* Effects */}
133          <button className="flex flex-col items-center gap-1">
134            <div className="w-12 h-12 bg-gray-800 rounded-full flex items-center justify-center text-2xl">
135136            </div>
137            <span className="text-xs text-white">Effects</span>
138          </button>
139
140          {/* Record Button */}
141          <button
142            onClick={isRecording ? stopRecording : startRecording}
143            className={`w-20 h-20 rounded-full border-4 flex items-center justify-center transition ${
144              isRecording
145                ? 'border-red-500 bg-red-500'
146                : 'border-white bg-transparent'
147            }`}
148          >
149            {isRecording && (
150              <div className="w-8 h-8 bg-white rounded"></div>
151            )}
152          </button>
153
154          {/* Sounds */}
155          <button className="flex flex-col items-center gap-1">
156            <div className="w-12 h-12 bg-gray-800 rounded-full flex items-center justify-center text-2xl">
157              🎵
158            </div>
159            <span className="text-xs text-white">Sounds</span>
160          </button>
161        </div>
162      </div>
163    </div>
164  );
165}

Summary 🎓

Sounds library - discover, search, trending sounds ✅ Sound card - preview, play, use sound ✅ Use count - how many videos use the sound ✅ Video recorder - camera access, recording, duration ✅ Effects - AR effects, filters, beauty mode ✅ Recording controls - start/stop, duration bar ✅ Sound selection - pick audio track for video ✅ Duration limits - 15s, 60s, 180s options

In the next exercise we'll add duet, stitch, and advanced editing!

See you soon! 🚀

Go to CodeWorlds