DEV Community

motorcyclediaries.fun
motorcyclediaries.fun

Posted on

We Built a Full Meme Contest Platform in One Day — Here's Every Feature We Shipped

Day 2 Dev Log: From Zero to a Complete Meme Contest Ecosystem

Yesterday we had a basic meme upload. Today we shipped a full-stack meme contest platform with voting, sharing, score tracking, social media integration, dashboards, badges, and more — all in a single day.

This is the raw, unfiltered dev log of everything we built for motorcyclediaries.fun/memes.


What We Started With

A simple meme page where users could upload images. That's it. No voting, no scoring, no sharing, no stats.

By the end of the day, here's what we shipped:


1. Animated Score Bar System on Top 3 Podium

The Top 3 memes now have a live animated score bar underneath each card.

How it works:

  • Every like pushes the bar forward (green → cyan gradient)
  • Every dislike pushes it back (red → orange gradient)
  • When a vote happens, the bar pulses with a glow animation (barPulse)
  • The score number does a pop-scale effect (scorePop)
  • All updates happen in real-time — no page refresh needed
@keyframes barPulse {
  0%   { box-shadow: 0 0 0 rgba(57,255,20,0); }
  50%  { box-shadow: 0 0 12px rgba(57,255,20,.4); }
  100% { box-shadow: 0 0 0 rgba(57,255,20,0); }
}

.score-bar {
  transition: width .8s cubic-bezier(.4,0,.2,1);
}
Enter fullscreen mode Exit fullscreen mode

The score bar percentage is calculated as:

const pct = Math.max(2, Math.min(100, 
  ((score + MAX_SCORE) / (MAX_SCORE * 2)) * 100
));
Enter fullscreen mode Exit fullscreen mode

This maps a score range of -MAX_SCORE to +MAX_SCORE onto a 2%–100% bar width. Even negative scores still show a small sliver so the bar never fully disappears.


2. Video & GIF Support

Memes aren't just images anymore. We added full support for:

  • GIF — animated memes
  • MP4 — video memes up to 10MB
  • WebM — web-optimized video

Backend changes (PHP):

$allowed_types = [
  'image/jpeg', 'image/png', 'image/gif', 
  'image/webp', 'video/mp4', 'video/webm'
];
Enter fullscreen mode Exit fullscreen mode

Frontend rendering:
Videos auto-play muted and loop in both the podium and grid views:

function isVideo(url) { return /\.(mp4|webm)$/i.test(url); }

const media = isVid 
  ? `<video class="pc-media" src="${m.url}" muted loop autoplay playsinline></video>` 
  : `<img class="pc-media" src="${m.url}" alt="Meme">`;
Enter fullscreen mode Exit fullscreen mode

The lightbox viewer also handles video — pause on close, controls visible.

CSP Headers Updated:

media-src 'self' blob:;
img-src 'self' data: blob:;
Enter fullscreen mode Exit fullscreen mode

3. Social Media Share System with Bonus Points

This was the big feature. Every meme can now be shared on X/Twitter, Telegram, or copied as a link.

Each share gives the meme +2 bonus score points.

The scoring formula:

Total Score = (Likes - Dislikes) + (Shares × 2)
Enter fullscreen mode Exit fullscreen mode

Backend: Share Tracking API

New share action in our PHP API:

case 'share':
    $shareKey = 'ip:' . md5($ip) . ':' . $platform . ':' . $meme_id;
    if (isset($db['shares'][$shareKey])) {
        echo json_encode(['success' => true, 'bonus' => false]);
        exit;
    }
    $m['shares'] = ($m['shares'] ?? 0) + 1;
    $db['shares'][$shareKey] = [
        'platform' => $platform, 
        'time' => date('c')
    ];
Enter fullscreen mode Exit fullscreen mode

Anti-abuse: One bonus per platform per IP per meme. You can share on all 3 platforms = max +6 points per meme per person.

Frontend: Share Buttons with Effects

Every podium card and grid card gets share buttons:

  • 𝕏 Share — opens Twitter intent with pre-filled text
  • ✈️ Share — opens Telegram share dialog
  • 🔗 Copy — copies meme URL to clipboard

When a share earns bonus points:

  1. Button gets a shareGlow animation
  2. Score bar updates in real-time
  3. Particle explosion fires
  4. Score number pops
async function doShare(memeId, memeUrl, platform, btn) {
  // Open share window
  window.open(shareUrl(memeId, memeUrl, platform), '_blank');

  // Record share & get bonus
  const r = await fetch(API + '?action=share', {
    method: 'POST',
    body: JSON.stringify({ meme_id: memeId, platform })
  });
  const d = await r.json();

  if (d.success && d.bonus) {
    updatePodiumBarShare(memeId);
    spawnParticles(btn, 'like');
  }
}
Enter fullscreen mode Exit fullscreen mode

4. Open Graph & Twitter Card Meta Tags

The memes page had zero social preview when shared on any platform. We added full OG and Twitter Card tags:

<meta property="og:title" content="$MOTO Meme Contest | Motorcycle Diaries">
<meta property="og:description" content="Create the best $MOTO memes...">
<meta property="og:image" content="https://motorcyclediaries.fun/images/og-image.jpg">
<meta property="og:image:width" content="1024">
<meta property="og:image:height" content="1024">

<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="@motodiariesfun">
<meta name="twitter:image" content="https://motorcyclediaries.fun/images/og-image.jpg">
Enter fullscreen mode Exit fullscreen mode

Now when anyone shares motorcyclediaries.fun/memes on Twitter, Telegram, Discord, WhatsApp — they see a rich preview with title, description, and image.


5. Dashboard Meme Integration

This was the deepest feature. Connected wallet users can now see their complete meme activity inside their rider dashboard.

API: Enhanced /my Endpoint

The API now returns comprehensive stats:

{
  "memes": [...],
  "stats": {
    "total": 3,
    "total_likes": 12,
    "total_dislikes": 2,
    "total_shares": 5,
    "total_score": 20,
    "top3_count": 1
  }
}
Enter fullscreen mode Exit fullscreen mode

The score uses the unified memeScore() function:

function memeScore($m) {
    $likes = $m['likes'] ?? 0;
    $dislikes = $m['dislikes'] ?? 0;
    $shares = $m['shares'] ?? 0;
    return ($likes - $dislikes) + ($shares * 2);
}
Enter fullscreen mode Exit fullscreen mode

Dashboard: "Your Meme Activity" Card

On the dashboard overview, a new card appears showing:

  • Uploads / Likes / Shares counts
  • Total Score / Top 3 Finishes
  • Thumbnail grid of your uploaded memes (with score overlay)
  • Auto-earned tags and badges

Earned Tags & Badges System

Based on your meme activity, you automatically earn labels:

Badge Requirement
🖼️ Meme Creator Upload 1+ meme
❤️‍🔥 Popular Memer 10+ total likes
🚀 Viral Meme 50+ total likes
🏆 Top 3 Reach Top 3 ranking
📤 Share Master 5+ total shares

These appear as colored tag pills on the dashboard and activate corresponding badges in the Rewards section.

Hero Bar Tag

If you've uploaded any memes, a pink 🖼️ MEME CREATOR tag appears next to your wallet address in the dashboard header bar — visible at all times.

Memes Tab: Full Stats View

The dedicated Memes tab in the dashboard now shows:

  1. Complete stat overview with all metrics
  2. Grid of all your uploaded memes with per-meme stats (likes, dislikes, shares, score)
  3. Featured badge indicator on featured memes
  4. Upload form for new memes (now supports video too)

6. Improved Navigation

Desktop Sidebar

The sidebar got a significant readability upgrade:

  • Width: 180px220px
  • Font: Orbitron 0.6remOutfit 0.95rem (~60% larger)
  • Icons: 0.9rem1.15rem
  • Better padding and hover states

Mobile Tabs

Mobile navigation tabs were redesigned:

  • Font: 0.48rem0.75rem (~56% larger)
  • Added emoji icons: 🏠 Dashboard, 🏆 Grand Prix, 🤝 Referral, etc.
  • Better borders and active state styling
  • display:flex alignment for icon + text

7. Clean URL Routing

Added Apache rewrite rules so all pages work without .html extensions:

<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME}.html -f
    RewriteRule ^(.+)$ $1.html [L]
</IfModule>
Enter fullscreen mode Exit fullscreen mode

Now motorcyclediaries.fun/memes works cleanly instead of requiring /memes.html.


The Technical Architecture

Here's the full stack powering the meme contest:

Frontend:    Vanilla HTML/CSS/JS (zero frameworks)
Backend:     PHP API with JSON file storage
Storage:     Server filesystem for uploads
Voting:      IP-based (md5 hash), no wallet required
Sharing:     IP + platform tracking
Scoring:     (likes - dislikes) + (shares × 2)
Hosting:     Hostinger shared hosting
CDN:         Cloudflare
Cost:        ~$3/month
Enter fullscreen mode Exit fullscreen mode

Why JSON files instead of a database?

For a meme contest with hundreds (not millions) of entries, JSON files with LOCK_EX are simpler, faster to iterate, and require zero database setup. The entire data model fits in one file. When we outgrow it, migrating to SQLite or MySQL is trivial.

Why IP-based voting instead of wallet-based?

We want maximum participation. Requiring wallet connection just to vote kills engagement. IP-based (one vote per IP per meme) keeps it fair while being frictionless. Wallet connection is only needed for uploading memes.


What's Next

  • Weekly automated winner selection and reward distribution
  • Meme of the Week auto-feature on social channels
  • Enhanced dashboard analytics with graphs
  • Community-driven meme categories and tags

The Numbers

Metric Count
Features shipped today 7 major
API endpoints added/updated 3
CSS animations created 4
Lines of code changed ~800
Deployment cycles 12
Frameworks used 0
Total infrastructure cost ~$3/month

Try It Yourself

Upload a meme. Vote on others. Share for bonus points. See your stats on the dashboard.

Everything is live right now.


Final Thought

You don't need React. You don't need Next.js. You don't need a $50K infrastructure budget.

Sometimes all you need is vanilla HTML, a PHP file, a JSON storage, and the stubborn refusal to stop shipping.

Born to Ride. Forced to HODL. Never stop building.


This is part of the Motorcycle Diaries Dev Log series — documenting every step of building a crypto community project from scratch with $0 budget.

Top comments (0)