Skip to content

๐ŸŒ Give Your AI Internet Superpowers

Your AI has incredible skills, but itโ€™s missing one crucial superpower: access to current information! ๐ŸŒ

Right now your AI only knows things from its training data. But what if it could search the web in real-time to answer questions about anything happening right now - news, prices, weather, sports, research, and more?

What weโ€™re building: Internet-connected AI that can search the web in real-time and give you current information about anything!


๐ŸŽฏ From Limited Knowledge to Unlimited Information

Section titled โ€œ๐ŸŽฏ From Limited Knowledge to Unlimited Informationโ€

Current limitation: Your AI only knows information from its training data
New superpower: Your AI can search the entire internet for current information!

Before (Limited AI):

User: "What's Tesla's current stock price?"
AI: "I don't have access to real-time stock prices" ๐Ÿ˜•

After (Internet-Connected AI):

User: "What's Tesla's current stock price?"
AI: [Searches web] "Tesla (TSLA) is currently trading at $248.50, up 3.2% today" ๐Ÿ“ˆ

The magic: Your AI becomes a real-time research assistant with access to all current information!

Real-world scenarios your connected AI will handle:

  • ๐Ÿ“ˆ Live data - โ€œWhatโ€™s Bitcoinโ€™s price?โ€ โ†’ Real-time cryptocurrency values
  • ๐ŸŒฆ๏ธ Weather updates - โ€œWill it rain tomorrow?โ€ โ†’ Current weather forecasts
  • ๐Ÿ“ฐ Breaking news - โ€œWhatโ€™s happening in tech?โ€ โ†’ Latest industry developments
  • ๐Ÿˆ Sports scores - โ€œHow did the game end?โ€ โ†’ Live game results
  • ๐Ÿ” Research - โ€œLatest AI breakthroughs?โ€ โ†’ Recent scientific discoveries

Static AI vs. Connected AI:

โŒ Static: "I don't know about events after my training"
โœ… Connected: "Let me search for the latest information..."
โŒ Static: Potentially outdated information
โœ… Connected: Always current, real-time data
โŒ Static: Limited to training knowledge
โœ… Connected: Access to the entire internet

Before we write any code, letโ€™s understand what web search integration actually means and why it transforms your AI.

Web search integration is like giving your AI access to the worldโ€™s largest library thatโ€™s constantly being updated. Instead of being limited to old knowledge, your AI can search for current information on any topic.

Real-world analogy: Itโ€™s like the difference between asking someone with a 2-year-old encyclopedia versus someone who can instantly search Google, Wikipedia, news sites, and databases for the most current information.

You already have powerful AI capabilities, but web search is different:

๐Ÿ’ฌ Regular Chat - AI responds with training knowledge (static information)
๐ŸŒ Web Search - AI searches internet for current data (live information)

๐ŸŽค Voice/Audio - AI processes what it knows (internal capabilities)
๐ŸŒ Web Search - AI finds new information in real-time (external access)

The key difference: Web search allows your AI to access information that didnโ€™t exist when it was trained, making it always current and comprehensive.

Your web search integration will use OpenAIโ€™s built-in web search capabilities:

๐Ÿ” OpenAI Web Search - The Real-Time Information Gateway

  • Best for: Getting current information about any topic
  • Strengths: Real-time data, reliable sources, integrated with AI reasoning
  • Use cases: News, prices, weather, research, current events
  • Think of it as: Your AIโ€™s window to the internet

Key capabilities:

  • Real-time search across the entire web
  • Source attribution with URLs and citations
  • Intelligent filtering of relevant information
  • Seamless integration with AI responses

Letโ€™s add web search to your existing backend using the same patterns you learned in previous modules. Weโ€™ll add new routes to handle web search-powered AI interactions.

Building on your foundation: You already have a working Node.js server with OpenAI integration. Weโ€™re simply adding web search capabilities to what youโ€™ve built.

Before writing code, letโ€™s understand what data our web search system needs to manage:

// ๐Ÿง  WEB SEARCH STATE CONCEPTS:
// 1. Search Queries - What the AI decides to search for
// 2. Search Results - Information found on the web
// 3. Source Attribution - Keeping track of where information came from
// 4. Result Analysis - AI processing of search results
// 5. Response Generation - Combining search data with AI reasoning

Key web search concepts:

  • Query Generation: AI deciding what to search for based on user questions
  • Search Execution: Using OpenAIโ€™s web search tool to find information
  • Result Processing: Analyzing and filtering search results
  • Source Citation: Providing sources for information found

Add this new endpoint to your existing index.js file, right after your function calling routes:

// ๐ŸŒ WEB SEARCH ENDPOINT: Add this to your existing server
app.post("/api/websearch/chat", async (req, res) => {
try {
// ๐Ÿ›ก๏ธ VALIDATION: Check required inputs
const { message, context = "[]", includeSearch = true } = req.body;
if (!message?.trim()) {
return res.status(400).json({
error: "Message is required",
success: false
});
}
console.log(`๐ŸŒ Web search chat: ${message}`);
// ๐Ÿ“ CONVERSATION CONTEXT: Parse existing conversation history
let conversationHistory = [];
try {
conversationHistory = JSON.parse(context);
} catch (error) {
console.log("Starting new conversation");
}
// ๐Ÿ› ๏ธ TOOLS SETUP: Define web search tool
const tools = includeSearch ? [
{
type: "web_search"
}
] : [];
// ๐Ÿค– AI WEB SEARCH: Process with OpenAI Chat API
const response = await openai.chat.completions.create({
model: "gpt-4o-mini",
messages: [
{
role: "system",
content: includeSearch
? "You are a helpful AI assistant with access to real-time web search. When users ask about current events, recent information, or anything that might require up-to-date data, use web search to find accurate, current information. Always cite your sources when using search results."
: "You are a helpful AI assistant. Answer questions using your training knowledge."
},
...conversationHistory,
{
role: "user",
content: message.trim()
}
],
tools: tools,
tool_choice: includeSearch ? "auto" : "none"
});
// ๐Ÿ”„ SEARCH RESULTS PROCESSING: Extract search information
let searchResults = [];
let finalResponse = response.choices[0].message.content;
let toolCalls = [];
// Check if web search was used
if (response.choices[0].message.tool_calls) {
console.log(`๐ŸŒ AI used web search, found ${response.choices[0].message.tool_calls.length} tool calls`);
for (const toolCall of response.choices[0].message.tool_calls) {
if (toolCall.type === 'function' && toolCall.function.name === 'web_search') {
toolCalls.push({
id: toolCall.id,
type: 'web_search',
query: JSON.parse(toolCall.function.arguments).query || "search query",
timestamp: new Date().toISOString()
});
}
}
}
// ๐Ÿ”„ CONVERSATION UPDATE: Update conversation history
const updatedHistory = [
...conversationHistory,
{
role: "user",
content: message.trim()
},
{
role: "assistant",
content: finalResponse
}
];
// ๐Ÿ“ค SUCCESS RESPONSE: Send web search results
res.json({
success: true,
message: finalResponse,
search_used: toolCalls.length > 0,
search_queries: toolCalls,
conversation_history: updatedHistory,
model: "gpt-4o-mini",
timestamp: new Date().toISOString()
});
} catch (error) {
// ๐Ÿšจ ERROR HANDLING: Handle web search failures
console.error("Web search error:", error);
res.status(500).json({
error: "Failed to process web search request",
details: error.message,
success: false
});
}
});
// ๐Ÿ” SEARCH TEST ENDPOINT: Test web search capability
app.post("/api/websearch/test", async (req, res) => {
try {
const { query = "positive news today" } = req.body;
console.log(`๐ŸŒ Testing web search with query: ${query}`);
const response = await openai.chat.completions.create({
model: "gpt-4o-mini",
messages: [
{
role: "system",
content: "You are a helpful AI assistant with web search capabilities. Use web search to find current information."
},
{
role: "user",
content: `Search the web for: ${query}`
}
],
tools: [{ type: "web_search" }],
tool_choice: "auto"
});
res.json({
success: true,
query: query,
response: response.choices[0].message.content,
tool_calls: response.choices[0].message.tool_calls || [],
model: "gpt-4o-mini",
timestamp: new Date().toISOString()
});
} catch (error) {
console.error("Web search test error:", error);
res.status(500).json({
error: "Failed to test web search",
details: error.message,
success: false
});
}
});
// ๐Ÿ“ฐ SEARCH SUGGESTIONS ENDPOINT: Get search suggestions
app.get("/api/websearch/suggestions", (req, res) => {
try {
const suggestions = [
{
category: "Current Events",
queries: [
"What positive news happened today?",
"Latest breakthrough in AI research",
"Recent space exploration achievements",
"Good news stories from this week"
]
},
{
category: "Real-time Data",
queries: [
"Current Bitcoin price",
"Today's weather in major cities",
"Latest stock market performance",
"Current exchange rates"
]
},
{
category: "Recent Developments",
queries: [
"New technology announcements this month",
"Recent scientific discoveries",
"Latest movie releases and reviews",
"Recent sports championship results"
]
},
{
category: "Trending Topics",
queries: [
"What's trending on social media today?",
"Popular viral videos this week",
"Trending news topics right now",
"Most discussed events recently"
]
}
];
res.json({
success: true,
suggestions: suggestions,
count: suggestions.reduce((total, cat) => total + cat.queries.length, 0),
timestamp: new Date().toISOString()
});
} catch (error) {
console.error("Search suggestions error:", error);
res.status(500).json({
error: "Failed to get search suggestions",
details: error.message,
success: false
});
}
});

Function breakdown:

  1. Web search integration - Use OpenAIโ€™s web_search tool
  2. Message processing - Handle user requests with real-time search capability
  3. Search result tracking - Monitor what searches the AI performs
  4. Source attribution - Track search queries and timing
  5. Conversation management - Maintain context with search-enhanced responses

๐Ÿ”ง Step 3: Building the React Web Search Component

Section titled โ€œ๐Ÿ”ง Step 3: Building the React Web Search Componentโ€

Now letโ€™s create a React component for web search using the same patterns from your existing components.

Create a new file src/WebSearch.jsx:

import { useState, useRef, useEffect } from "react";
import { Send, Search, Globe, ExternalLink, MessageSquare, Download, Trash2, Lightbulb } from "lucide-react";
function WebSearch() {
// ๐Ÿง  STATE: Web search data management
const [message, setMessage] = useState(""); // User input
const [conversation, setConversation] = useState([]); // Chat history
const [isProcessing, setIsProcessing] = useState(false); // Processing status
const [searchEnabled, setSearchEnabled] = useState(true); // Search toggle
const [searchQueries, setSearchQueries] = useState([]); // Recent search queries
const [suggestions, setSuggestions] = useState([]); // Search suggestions
const [error, setError] = useState(null); // Error messages
const chatEndRef = useRef(null);
// ๐Ÿ”ง FUNCTIONS: Web search logic engine
// Load search suggestions on component mount
useEffect(() => {
loadSearchSuggestions();
}, []);
// Auto-scroll to bottom of chat
useEffect(() => {
chatEndRef.current?.scrollIntoView({ behavior: "smooth" });
}, [conversation]);
// Load search suggestions
const loadSearchSuggestions = async () => {
try {
const response = await fetch("http://localhost:8000/api/websearch/suggestions");
const data = await response.json();
if (data.success) {
setSuggestions(data.suggestions);
}
} catch (error) {
console.error('Failed to load suggestions:', error);
}
};
// Send message with web search capability
const sendMessage = async () => {
if (!message.trim() || isProcessing) return;
const userMessage = message.trim();
setMessage("");
setIsProcessing(true);
setError(null);
// Add user message to conversation immediately
const newConversation = [
...conversation,
{ role: "user", content: userMessage, timestamp: new Date().toISOString() }
];
setConversation(newConversation);
try {
// ๐Ÿ“ค API CALL: Send to web search endpoint
const response = await fetch("http://localhost:8000/api/websearch/chat", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
message: userMessage,
context: JSON.stringify(conversation),
includeSearch: searchEnabled
}),
});
const data = await response.json();
if (!response.ok) {
throw new Error(data.error || 'Failed to process message');
}
// โœ… SUCCESS: Update conversation and search results
setConversation(data.conversation_history.map(msg => ({
...msg,
timestamp: msg.timestamp || new Date().toISOString()
})));
// Track search queries if any were made
if (data.search_used && data.search_queries && data.search_queries.length > 0) {
setSearchQueries(prev => [...data.search_queries, ...prev].slice(0, 20)); // Keep last 20 queries
}
} catch (error) {
console.error('Web search failed:', error);
setError(error.message || 'Something went wrong while processing your message');
// Add error message to conversation
setConversation(prev => [...prev, {
role: "assistant",
content: "I'm sorry, I encountered an error while processing your request. Please try again.",
timestamp: new Date().toISOString(),
error: true
}]);
} finally {
setIsProcessing(false);
}
};
// Send a suggested query
const sendSuggestion = (query) => {
setMessage(query);
// Auto-send the suggestion
setTimeout(() => {
if (query.trim() && !isProcessing) {
const event = { target: { value: query } };
setMessage(query);
sendMessage();
}
}, 100);
};
// Test web search functionality
const testWebSearch = async () => {
setIsProcessing(true);
setError(null);
try {
const response = await fetch("http://localhost:8000/api/websearch/test", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
query: "positive news today"
}),
});
const data = await response.json();
if (!response.ok) {
throw new Error(data.error || 'Test failed');
}
// Add test results to conversation
setConversation(prev => [...prev,
{
role: "user",
content: "๐Ÿงช Test web search functionality",
timestamp: new Date().toISOString()
},
{
role: "assistant",
content: data.response,
timestamp: new Date().toISOString(),
test: true
}
]);
// Add search queries
if (data.tool_calls && data.tool_calls.length > 0) {
const queries = data.tool_calls.map(call => ({
id: call.id || Date.now(),
type: 'web_search',
query: "positive news today",
timestamp: new Date().toISOString()
}));
setSearchQueries(prev => [...queries, ...prev].slice(0, 20));
}
} catch (error) {
console.error('Web search test failed:', error);
setError(error.message || 'Web search test failed');
} finally {
setIsProcessing(false);
}
};
// Handle Enter key press
const handleKeyPress = (e) => {
if (e.key === "Enter" && !e.shiftKey && !isProcessing) {
e.preventDefault();
sendMessage();
}
};
// Clear conversation
const clearConversation = () => {
setConversation([]);
setSearchQueries([]);
setError(null);
};
// Download conversation with search results
const downloadConversation = () => {
const exportData = {
conversation: conversation,
search_queries: searchQueries,
search_enabled: searchEnabled,
timestamp: new Date().toISOString()
};
const element = document.createElement('a');
const file = new Blob([JSON.stringify(exportData, null, 2)], { type: 'application/json' });
element.href = URL.createObjectURL(file);
element.download = `web-search-session-${Date.now()}.json`;
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
};
// ๐ŸŽจ UI: Interface components
return (
<div className="min-h-screen bg-gradient-to-br from-green-50 to-blue-50 flex items-center justify-center p-4">
<div className="bg-white rounded-2xl shadow-2xl w-full max-w-6xl flex flex-col overflow-hidden">
{/* Header */}
<div className="bg-gradient-to-r from-green-600 to-blue-600 text-white p-6">
<div className="flex items-center justify-between">
<div className="flex items-center space-x-3">
<div className="w-10 h-10 bg-white bg-opacity-20 rounded-full flex items-center justify-center">
<Globe className="w-5 h-5" />
</div>
<div>
<h1 className="text-xl font-bold">๐ŸŒ AI Web Search</h1>
<p className="text-green-100 text-sm">AI with real-time internet access!</p>
</div>
</div>
<div className="text-right">
<div className="flex items-center space-x-3">
<label className="flex items-center space-x-2 text-green-100">
<input
type="checkbox"
checked={searchEnabled}
onChange={(e) => setSearchEnabled(e.target.checked)}
className="rounded"
/>
<span className="text-sm">Web Search</span>
</label>
<button
onClick={testWebSearch}
disabled={isProcessing}
className="px-3 py-1 bg-white bg-opacity-20 rounded-lg hover:bg-opacity-30 transition-colors duration-200 text-sm"
>
Test Search
</button>
</div>
</div>
</div>
</div>
{/* Search Suggestions */}
{conversation.length === 0 && suggestions.length > 0 && (
<div className="p-4 bg-gray-50 border-b border-gray-200">
<h3 className="font-semibold text-gray-900 mb-3 flex items-center text-sm">
<Lightbulb className="w-4 h-4 mr-2 text-green-600" />
Try These Web Search Examples
</h3>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-3">
{suggestions.slice(0, 2).map((category) => (
<div key={category.category} className="space-y-2">
<h4 className="font-medium text-gray-700 text-xs">{category.category}</h4>
{category.queries.slice(0, 2).map((query, idx) => (
<button
key={idx}
onClick={() => sendSuggestion(query)}
disabled={isProcessing}
className="w-full text-left p-2 bg-white rounded border border-gray-200 hover:border-green-300 hover:bg-green-50 transition-colors duration-200 text-xs"
>
{query}
</button>
))}
</div>
))}
</div>
</div>
)}
{/* Chat Area */}
<div className="flex-1 flex flex-col min-h-0">
<div className="flex-1 overflow-y-auto p-6 space-y-4">
{conversation.length === 0 ? (
<div className="text-center py-12">
<div className="w-16 h-16 bg-green-100 rounded-2xl flex items-center justify-center mx-auto mb-4">
<Globe className="w-8 h-8 text-green-600" />
</div>
<h3 className="text-lg font-semibold text-gray-700 mb-2">
Ready for Web Search!
</h3>
<p className="text-gray-600 max-w-md mx-auto mb-4">
Ask me about current events, recent news, or anything that requires up-to-date information.
</p>
<div className="text-sm text-gray-500 space-y-1">
<p>๐Ÿ’ก "What positive news happened today?"</p>
<p>๐Ÿ’ก "Current Bitcoin price"</p>
<p>๐Ÿ’ก "Latest AI research breakthroughs"</p>
</div>
</div>
) : (
<>
{conversation.map((msg, index) => (
<div
key={index}
className={`flex ${msg.role === 'user' ? 'justify-end' : 'justify-start'}`}
>
<div
className={`max-w-3xl px-4 py-3 rounded-lg ${
msg.role === 'user'
? 'bg-green-500 text-white'
: msg.error
? 'bg-red-50 text-red-700 border border-red-200'
: msg.test
? 'bg-blue-50 text-blue-700 border border-blue-200'
: 'bg-gray-100 text-gray-900'
}`}
>
<p className="whitespace-pre-wrap">{msg.content}</p>
<p className="text-xs opacity-70 mt-1">
{new Date(msg.timestamp).toLocaleTimeString()}
{msg.test && " โ€ข Test"}
</p>
</div>
</div>
))}
{isProcessing && (
<div className="flex justify-start">
<div className="bg-gray-100 text-gray-900 px-4 py-3 rounded-lg">
<div className="flex items-center space-x-2">
<Search className="w-4 h-4 text-green-600 animate-spin" />
<span className="text-sm">Searching the web and analyzing results...</span>
</div>
</div>
</div>
)}
<div ref={chatEndRef} />
</>
)}
</div>
{/* Search Queries Log */}
{searchQueries.length > 0 && (
<div className="border-t border-gray-200 p-4 bg-gray-50">
<h4 className="font-semibold text-gray-900 mb-2 text-sm flex items-center">
<Search className="w-4 h-4 mr-2 text-green-600" />
Recent Search Queries ({searchQueries.length})
</h4>
<div className="space-y-2 max-h-32 overflow-y-auto">
{searchQueries.slice(0, 3).map((query, index) => (
<div key={index} className="bg-white rounded p-2 border border-gray-200 text-xs">
<div className="flex items-start justify-between">
<div className="flex-1">
<h5 className="font-medium text-gray-900 mb-1">Web Search Query</h5>
<p className="text-gray-600 text-xs">{query.query || "Search performed"}</p>
<span className="text-gray-500 text-xs">
{new Date(query.timestamp).toLocaleTimeString()}
</span>
</div>
</div>
</div>
))}
</div>
</div>
)}
{/* Error Display */}
{error && (
<div className="border-t border-red-200 bg-red-50 p-4">
<p className="text-red-700 text-sm">
<strong>Error:</strong> {error}
</p>
</div>
)}
{/* Input Area */}
<div className="border-t border-gray-200 p-4">
<div className="flex items-center space-x-3">
<div className="flex-1">
<textarea
value={message}
onChange={(e) => setMessage(e.target.value)}
onKeyPress={handleKeyPress}
rows="2"
placeholder={searchEnabled ? "Ask about current events, recent news, or real-time information..." : "Ask me anything (web search disabled)..."}
disabled={isProcessing}
className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-green-500 focus:border-transparent transition-all duration-200 resize-none disabled:bg-gray-100"
/>
</div>
<div className="space-y-2">
<button
onClick={sendMessage}
disabled={isProcessing || !message.trim()}
className="px-6 py-3 bg-gradient-to-r from-green-600 to-blue-600 hover:from-green-700 hover:to-blue-700 disabled:from-gray-300 disabled:to-gray-300 text-white rounded-lg transition-all duration-200 flex items-center space-x-2 shadow-lg disabled:shadow-none"
>
<Send className="w-4 h-4" />
<span>Send</span>
</button>
{conversation.length > 0 && (
<div className="flex space-x-1">
<button
onClick={downloadConversation}
className="p-2 bg-gray-100 text-gray-600 rounded-lg hover:bg-gray-200 transition-colors duration-200"
title="Download conversation"
>
<Download className="w-4 h-4" />
</button>
<button
onClick={clearConversation}
className="p-2 bg-red-100 text-red-600 rounded-lg hover:bg-red-200 transition-colors duration-200"
title="Clear conversation"
>
<Trash2 className="w-4 h-4" />
</button>
</div>
)}
</div>
</div>
</div>
</div>
</div>
</div>
);
}
export default WebSearch;

Letโ€™s test your web search feature step by step to make sure everything works correctly.

First, verify your backend routes work:

Test web search endpoint:

Terminal window
curl -X POST http://localhost:8000/api/websearch/chat \
-H "Content-Type: application/json" \
-d '{"message": "What positive news happened today?", "includeSearch": true}'

Test search suggestions endpoint:

Terminal window
curl http://localhost:8000/api/websearch/suggestions

Start both servers:

Backend (in your backend folder):

Terminal window
npm run dev

Frontend (in your frontend folder):

Terminal window
npm run dev

Test the complete flow:

  1. Navigate to Web Search โ†’ Click the โ€œWebโ€ tab in navigation
  2. Review search suggestions โ†’ See example queries for current events
  3. Test current events โ†’ Ask โ€œWhat positive news happened today?โ€
  4. Test real-time data โ†’ Ask โ€œCurrent Bitcoin priceโ€
  5. Test recent information โ†’ Ask โ€œLatest AI research breakthroughsโ€
  6. Try search suggestions โ†’ Click on suggested queries
  7. Toggle search on/off โ†’ Disable web search and see difference
  8. Review search queries โ†’ See log of searches performed
  9. Download conversation โ†’ Export chat with search results

Test error scenarios:

โŒ Search disabled: Turn off web search and ask about current events
โŒ Network error: Disconnect internet during search
โŒ Invalid query: Send empty or malformed messages
โŒ Rate limiting: Send multiple rapid requests

Expected behavior:

  • Clear indication when search is enabled/disabled
  • Graceful fallback when search fails
  • Proper error messages for issues
  • Conversation continues after errors

Congratulations! Youโ€™ve extended your existing application with complete AI web search:

  • โœ… Extended your backend with OpenAI web search tool integration
  • โœ… Added React web search component following the same patterns as your other features
  • โœ… Implemented real-time internet access for current information and events
  • โœ… Created search query tracking with detailed logs
  • โœ… Added search suggestions with categorized example queries
  • โœ… Maintained consistent design with your existing application

Your application now has:

  • Text chat with streaming responses
  • Image generation with DALL-E 3
  • Audio transcription with Whisper voice recognition
  • File analysis with intelligent document processing
  • Text-to-speech with natural voice synthesis
  • Vision analysis with GPT-4o visual intelligence
  • Voice interaction with GPT-4o Audio natural conversations
  • Function calling with real-world tool integration
  • Web search with real-time internet access
  • Unified navigation between all features
  • Professional UI with consistent styling

Next up: Youโ€™ll learn about Structured Output, where your AI responses follow strict schemas and formats using Zod parsing - perfect for building reliable data processing and API integrations.

Your OpenAI mastery application now has real-time internet access! ๐ŸŒ