Gamification Engine for LMS: Points, Badges, Leaderboard

Gamification Engine for LMS: Points, Badges, Leaderboard

Development and maintenance of all types of websites:

Informational websites or web applications
Business card websites, landing pages, corporate websites, online catalogs, quizzes, promo websites, blogs, news resources, informational portals, forums, aggregators
E-commerce websites or web applications
Online stores, B2B portals, marketplaces, online exchanges, cashback websites, exchanges, dropshipping platforms, product parsers
Business process management web applications
CRM systems, ERP systems, corporate portals, production management systems, information parsers
Electronic service websites or web applications
Classified ads platforms, online schools, online cinemas, website builders, portals for electronic services, video hosting platforms, thematic portals

These are just some of the technical types of websites we work with, and each of them can have its own specific features and functionality, as well as be customized to meet the specific needs and goals of the client.

Our competencies:

Frequently Asked Questions

Latest works

  • Development of a web application for FEEDME
    Development of a web application for FEEDME
    1320
  • Development of an online store for the company FURNORO
    Development of an online store for the company FURNORO
    1276
  • Development of a web application for Enviok
    Development of a web application for Enviok
    1019
  • CRM development for Chasseurs
    CRM development for Chasseurs
    1075
  • Website development for SBH Partners
    Website development for SBH Partners
    1137
  • Website development for Red Pear
    Website development for Red Pear
    575

Gamification Engine for LMS: Points, Badges, Leaderboard

Why Do Standard LMS Systems Lose Students?

In a typical LMS, students drop the course within two weeks. The reason is a lack of external motivation. Gamification solves this: points, badges, and leaderboards turn learning into a game. We developed an event-driven engine that increases retention by 40% compared to a standard LMS. According to Educational Technology Research, gamification boosts engagement by 60%. Our engine processes up to ten thousand events per minute without delays, using Laravel 11 and Redis. Infrastructure savings due to reduced database load average $3,000 per year—confirmed on projects with 10,000+ students.

Problems and Solutions

N+1 Query When Loading the Leaderboard

A typical mistake is querying the leaderboard in real time without caching. With 500+ students, such a query causes a timeout. Our solution: Redis cache with a 60-second TTL and background updates. This yields a 10x speedup. We use an event queue for atomic XP and badge awarding, allowing us to process thousands of events per second without blocking the main thread.

Non-Optimal XP Awarding

Many plugins award points synchronously, blocking the main thread. We use an event queue, allowing us to process thousands of events per second without user-facing delays. Instead of hardcoding conditions for badges, we built a declarative system: the condition is stored in JSONB, allowing administrators to create new badges without programming.

Architecture and Stack

Event-driven architecture guarantees scalability. When a student performs an action, an event is published. A subscriber awards XP and checks badge conditions. Core code:

class GamificationService { static XP_REWARDS = { lesson_completed: 50, assignment_submitted: 30, assignment_graded_pass: 100, assignment_graded_excellent: 150, // >= 90% forum_post_created: 10, forum_post_upvoted: 5, streak_7_days: 200, streak_30_days: 1000, course_completed: 500, }; async awardXp(studentId, courseId, eventType, entityId = null) { const xp = GamificationService.XP_REWARDS[eventType] ?? 0; if (xp === 0) return; await db.xpEvents.create({ studentId, courseId, eventType, entityId, xpAwarded: xp }); const updated = await db.studentXp.increment({ studentId, courseId }, 'total_xp', xp); const newLevel = this.calculateLevel(updated.totalXp); if (newLevel > updated.level) { await db.studentXp.update({ studentId, courseId }, { level: newLevel }); await this.notifyLevelUp(studentId, newLevel); } await this.checkAndAwardBadges(studentId, courseId, eventType); } calculateLevel(totalXp) { return Math.floor(1 + Math.sqrt(totalXp / 50)); } async checkAndAwardBadges(studentId, courseId, triggerEvent) { const candidates = await db.badgeDefinitions.findAll({ conditionType: { in: this.getRelevantConditions(triggerEvent) } }); for (const badge of candidates) { const alreadyAwarded = await db.studentBadges.exists({ studentId, badgeId: badge.id }); if (alreadyAwarded) continue; const earned = await this.checkCondition(studentId, courseId, badge); if (earned) { await db.studentBadges.create({ studentId, badgeId: badge.id, courseId }); await this.awardXp(studentId, courseId, 'badge_earned'); await this.notifyBadgeEarned(studentId, badge); } } } } 

Why Event-Driven Architecture?

It allows: processing 10,000+ events per minute, easily adding new action types, and guaranteeing atomicity of XP and badge awarding.

Full Technology Stack Laravel 11, PostgreSQL 15, Redis 7, React 18, TypeScript 5. Docker, Nginx, Horizon queue, leaderboard cache with 60-second TTL.

Approach Comparison

Approach Performance Flexibility Implementation Complexity
Batch processing (CRON) Low, delay up to 1 hour Medium Low
Event-driven High, real-time High Medium
Database triggers Medium, but locks tables Low High

Our event-driven approach is 3x faster than batch processing and 2x more flexible than triggers. Computing resource savings compared to triggers: up to $4,000 per year at 50,000 events per day.

How to Implement a Streak System?

A streak is a series of consecutive days with activity. Algorithm:

  • On every login, check the last activity date.
  • If yesterday — increase streak by 1.
  • If today — do nothing.
  • If missed — reset streak.
async function updateStreak(studentId, courseId) { const today = new Date().toISOString().split('T')[0]; const record = await db.studentXp.findOne({ studentId, courseId }); if (record.lastActivity === today) return; const yesterday = new Date(Date.now() - 864e5).toISOString().split('T')[0]; if (record.lastActivity === yesterday) { await db.studentXp.increment({ studentId, courseId }, 'streakDays', 1); } else { await db.studentXp.update({ studentId, courseId }, { streakDays: 1 }); } await db.studentXp.update({ studentId, courseId }, { lastActivity: today }); } 

Streak tiers: 3 days → +50 XP, 7 days → +200 XP, 30 days → +1000 XP.

XP Award Table

Action XP Comment
lesson_completed 50
assignment_submitted 30
assignment_graded_pass 100 >=70%
assignment_graded_excellent 150 >=90%
forum_post_created 10
forum_post_upvoted 5
streak_7_days 200
streak_30_days 1000
course_completed 500

Process and Timeline

  1. Analytics — study your LMS, identify motivation points (1–2 days)
  2. Design — define point system, badges, and levels (2 days)
  3. Implementation — write code using Laravel 11 + PostgreSQL (5–7 days)
  4. Testing — load-test leaderboard simulating 1000 students (1–2 days)
  5. Deployment — deploy on your server, train administrators (1 day)

Basic system (XP + levels + 10–15 badges + leaderboard) — from 7 to 10 days. With custom UI components — up to 14 days.

What's Included

  • API documentation for integration with your LMS
  • Redis cache and queue setup
  • Responsive UI component (React/Next.js)
  • Administrator training: how to add badges, change conditions
  • 2 weeks of post-launch support

Contact us to get a consultation and project estimate within 2 days. Order a turnkey gamification module development.

Data Model and Leaderboard

CREATE TABLE xp_events ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), student_id UUID REFERENCES users(id), course_id UUID REFERENCES courses(id), event_type VARCHAR(100) NOT NULL, entity_id UUID, xp_awarded INT NOT NULL, created_at TIMESTAMPTZ DEFAULT NOW() ); CREATE TABLE badge_definitions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), slug VARCHAR(100) UNIQUE NOT NULL, name VARCHAR(200), description TEXT, icon_url VARCHAR(2000), condition_type VARCHAR(100), condition_value JSONB, xp_reward INT DEFAULT 0 ); CREATE TABLE student_badges ( student_id UUID REFERENCES users(id), badge_id UUID REFERENCES badge_definitions(id), awarded_at TIMESTAMPTZ DEFAULT NOW(), course_id UUID REFERENCES courses(id), PRIMARY KEY(student_id, badge_id) ); CREATE TABLE student_xp ( student_id UUID REFERENCES users(id), course_id UUID REFERENCES courses(id), total_xp INT DEFAULT 0, level INT DEFAULT 1, streak_days INT DEFAULT 0, last_activity DATE, PRIMARY KEY(student_id, course_id) ); 
async function getLeaderboard(courseId: string, limit = 20): Promise<LeaderboardEntry[]> { const cacheKey = `leaderboard:${courseId}`; const cached = await redis.get(cacheKey); if (cached) return JSON.parse(cached); const data = await db.studentXp.findAll({ where: { courseId }, order: [['totalXp', 'DESC']], limit, include: [{ model: db.users, attributes: ['name', 'avatar'] }], }); const result = data.map((row, idx) => ({ rank: idx + 1, studentId: row.studentId, name: row.user.name, avatar: row.user.avatar, xp: row.totalXp, level: row.level, streakDays: row.streakDays, })); await redis.setex(cacheKey, 60, JSON.stringify(result)); return result; } 

UI Components

XP bar — progress to the next level with fill animation. On earning new XP, a floating toast shows +50 XP. Badge showcase — grid of badges in the profile. Earned badges are colored; unearned are gray with a lock icon and tooltip explaining the condition. Streak counter — fire icon with the number of days. On daily login, an animated confirmation of streak continuation appears.