Get Plugin

Lucky Wheel

Lucky Wheel / Spin to Win

Interactive HTML5 canvas prize wheel with server-side outcome determination. Works in two modes: product-tied (quick setup on any raffle product) and standalone (dedicated admin page, usable anywhere in your store — cart, checkout, thank-you, popup, or shortcode).


Two Ways to Create a Wheel

Product WheelStandalone Wheel
Created viaProduct edit screen → PRO FeaturesWooCommerce → Lucky Wheels → Add New
Tied to a productYes — one raffle productNo — independent
Display on product pageAutomatic (before form, after form, or after summary)Via Display Rules or shortcode
Cart / Checkout / Thank-youNoYes
Popup / ModalNoYes
Email gateNoYes
Coupon scopeAuto-restricted to that productConfigurable — any product, category, or store-wide
Shortcode[raffle_wheel id="PRODUCT_ID"][raffle_wheel id="WHEEL_ID"]
Scroll to see all columns →

Product Wheel (Quick Setup)

Best for raffle owners who want a wheel on their product page with minimal configuration.

  • Edit a raffle product.
  • In the PRO Features section, check Lucky Wheel.
  • Choose Quick setup (configure slices inline) or Use existing wheel (link a standalone wheel).

Standalone Wheel

Best for store-wide marketing: cart abandonment recovery, checkout incentives, lead generation, or post-purchase re-engagement.

  • Go to WooCommerce → Lucky Wheels → Add New.
  • Give it a title (e.g. "Summer Sale Spinner").
  • Configure slices, display rules, coupon settings, and spin limits.
  • Publish.

Configuring Slices

Each slice of the wheel represents one possible outcome. A wheel needs at least 2 slices.

FieldDescription
ColorVisual color on the wheel (color picker)
LabelText displayed on the slice (e.g., "10% Off", "Free Ticket!")
WeightProbability weight. Higher number = more likely. Weights are relative to all slices.
Prize typeNo prize, Discount %, Discount (fixed), Free ticket, Custom message
Prize valueThe value: percentage, currency amount, or custom text
Scroll to see all columns →

Free Ticket (standalone wheels)

When using a standalone wheel, the Free Ticket prize type shows an additional Target Product dropdown. This lets you select which product the 100% discount coupon applies to. If left empty, the coupon has no product restriction.

Default slices

When enabling a wheel for the first time, these defaults are pre-populated:

LabelTypeWeightEffect
10% OffDiscount %2010% coupon
Try AgainNo prize35Better luck next time
25% OffDiscount %1025% coupon
Better LuckNo prize30Better luck next time
Free Ticket!Free ticket5100% discount coupon
5% OffDiscount %255% coupon
Scroll to see all columns →

Display Rules (Standalone Wheels)

Configure where standalone wheels appear automatically, without shortcodes.

Product pages

OptionBehaviour
Specific productsSelect one or more products. The wheel renders on those product pages.
All products in categoriesSelect categories. The wheel renders on every product in those categories.
Do not showThe wheel does not auto-display on any product page.
Scroll to see all columns →
When shown on product pages, the wheel renders before the add-to-cart form by default.

WooCommerce pages

OptionBehaviour
Cart pageRenders above the cart. Works with both classic (shortcode) and block-based cart.
Checkout pageRenders above the checkout form. Works with both classic and block-based checkout.
Thank-you pageRenders on the order confirmation page. Supports a minimum order total threshold — the wheel only shows if the order meets the minimum.
Scroll to see all columns →

Block & Classic compatibility

All placements detect whether WooCommerce is using the block-based cart/checkout (default since WooCommerce 8.3) or the legacy shortcode pages and hook in correctly for both.


Show the wheel as a popup triggered by user behavior.

Settings

SettingDescription
Enable popupToggle on/off
TriggerDelay (seconds after page load), Scroll (% scrolled), Exit intent (mouse leaves viewport)
DelaySeconds to wait (for delay trigger)
Scroll %Percentage of page scrolled (for scroll trigger)
Page scopeAll pages, WooCommerce pages only, or Shop & product pages only
CooldownDays before the popup shows again to the same visitor (stored in localStorage)
Scroll to see all columns →

How it works

  • The popup renders as a native HTML <dialog> element in the page footer.
  • The selected trigger fires on the appropriate event.
  • The dialog opens with a backdrop overlay and entrance animation.
  • The close button (×) or clicking outside dismisses it.
  • After closing, the cooldown prevents it from showing again for the configured number of days.

Coupon Settings

All auto-generated coupons share these characteristics:

  • Single-use (1 redemption)
  • Configurable expiry (default: 7 days, overridable per wheel or via global settings)
  • Optional minimum order amount
  • Described as "Lucky Wheel prize — [wheel name]"

Coupon scope (standalone wheels)

ScopeBehaviour
Store-wideNo product/category restriction
Specific productCoupon only valid for the selected product
Specific categoryCoupon only valid for products in the selected category
Scroll to see all columns →
For product-tied wheels, coupons are automatically scoped to that raffle product.

Auto-cleanup

Expired wheel-generated coupons are automatically trashed by a daily WP-Cron job. This can be toggled on/off in the global Pro settings.


Email Gate & Lead Generation

Require visitors to enter their email address before they can spin. Turns the wheel into a lead generation tool.

Enabling

  • Edit a standalone wheel.
  • In the Spin Limits meta box, check Require email before spinning.
  • Optionally enable the consent checkbox and customize the consent text (e.g. "I agree to receive marketing emails").

How it works

  • The wheel renders with an email form overlay instead of the Spin button.
  • The visitor enters their email (and ticks consent if required).
  • The email is validated and checked for duplicates (same email cannot spin the same wheel twice).
  • On success, the overlay disappears and the Spin button becomes active.
  • If the visitor wins a coupon, the coupon code is emailed to them using WooCommerce's branded email templates.

GDPR consent

When enabled, a checkbox with custom text appears below the email input. The form cannot be submitted without ticking it. Consent status is recorded in the database.

Integration hooks

After an email is collected, the action rfwc_pro_wheel_email_collected fires with four parameters:

do_action( 'rfwc_pro_wheel_email_collected', $email, $wheel_id, $row_id, $consent );

Use this to integrate with Mailchimp, Mailpoet, ConvertKit, or any other email service.

Admin: Collected Emails

Go to WooCommerce → Lucky Wheels → Collected Emails to view all collected emails.

FeatureDescription
Filter by wheelDropdown to show emails for a specific wheel
Pagination30 emails per page
CSV exportOne-click download of all emails (or filtered by wheel)
Scroll to see all columns →
CSV columns: Email, Wheel, Coupon Code, Consent (Yes/No), IP, Date.

Thank-You Page / Post-Purchase

Show the wheel on the order confirmation page as a re-engagement tool: "You just ordered — spin for a reward on your next purchase!"

Order-aware features

FeatureDescription
Purchase thresholdOnly show the wheel if the order total meets a configurable minimum
One spin per orderPrevents refresh abuse — each order can only spin each wheel once (tracked in order meta)
Customer-tied couponsWon coupons are email-restricted to the customer's billing email
Raffle-aware modeIf the order contains raffle products with their own wheel, that product-specific wheel shows first
Scroll to see all columns →

Sound Effects

When enabled, the wheel plays sounds using the Web Audio API (no external files):

EventSound
SpinningTick-tick as the pointer passes each slice
WinAscending fanfare
LoseDescending tone
Scroll to see all columns →
Sound can be toggled per wheel or set as a global default.

Spin Tracking

User typeStorage
Logged-in usersUser meta (_rfwc_pro_wheel_spins)
GuestsWooCommerce session
Thank-you pageOrder meta (_rfwc_pro_order_spun_wheels)
Scroll to see all columns →
The server enforces the max spins limit — even if the frontend button is visible, exceeding the limit returns an error.

Shortcode

[raffle_wheel id="123"]
AttributeValuesDefaultDescription
idPost IDRequiredProduct ID (for product wheels) or Wheel ID (for standalone wheels)
Scroll to see all columns →
The shortcode handler automatically detects whether the ID belongs to a product or a standalone wheel and renders accordingly.

Global Pro Settings

Go to WooCommerce → Settings → Raffle → Pro to configure store-wide defaults.

SettingDefaultDescription
Default Max Spins1Max spins per user for new wheels (0 = unlimited)
Default Coupon Expiry7 daysHow long wheel coupons last
Default Sound EffectsOnEnable sound by default
Auto-delete Expired CouponsOnDaily cron to trash expired wheel coupons
Default Email GateOffRequire email by default
Scroll to see all columns →
Per-wheel settings always override global defaults. Empty/unset per-wheel values fall back to the global value.

How Spinning Works (Technical)

  • Customer clicks Spin! (or submits email first if email gate is active).
  • An AJAX request is sent to the server with the wheel/product ID (and order ID if on thank-you page).
  • The server validates: wheel exists, user hasn't exceeded max spins, email gate passed, order not already spun.
  • A winning slice is picked using weighted random selection (random_int — cryptographically secure).
  • If the prize requires a coupon, a WooCommerce coupon is created with the configured settings.
  • The server returns the winning slice index and a rotation angle.
  • The frontend animates the wheel (4.5 second easing animation) to land on the winning slice.
  • A result card appears showing the outcome — with a copy-to-clipboard button for coupon codes.
Security: The outcome is always determined server-side. The frontend animation is purely visual — the wheel always lands on the server-determined slice.

Prize Types

TypeWhat happens
No prize"Better luck next time" message. No action taken.
Discount %A single-use WooCommerce coupon with the specified percentage off.
Discount (fixed)A single-use fixed-amount coupon.
Free ticketA 100% discount coupon for 1 unit (product wheels: that product; standalone: configurable target product).
Custom messageDisplays the configured text. No coupon generated. Useful for "Come back tomorrow" or "Follow us on Instagram" type prizes.
Scroll to see all columns →

CSS Classes

Wheel

ElementCSS Class
Container.rfwc-pro-lucky-wheel
Title.rfwc-pro-lucky-wheel__title
Canvas container.rfwc-pro-lucky-wheel__container
Canvas.rfwc-pro-lucky-wheel__canvas
Pointer.rfwc-pro-lucky-wheel__pointer
Spin button.rfwc-pro-lucky-wheel__spin-btn
Result container.rfwc-pro-lucky-wheel__result
Win card.rfwc-pro-lucky-wheel__result-card--win
Lose card.rfwc-pro-lucky-wheel__result-card--lose
Coupon code.rfwc-pro-lucky-wheel__coupon-code
Scroll to see all columns →

Email Gate

ElementCSS Class
Container.rfwc-pro-wheel-email-gate
Prompt text.rfwc-pro-wheel-email-gate__prompt
Form wrapper.rfwc-pro-wheel-email-gate__form
Email input.rfwc-pro-wheel-email-gate__input
Consent label.rfwc-pro-wheel-email-gate__consent
Submit button.rfwc-pro-wheel-email-gate__submit
Error message.rfwc-pro-wheel-email-gate__error
Scroll to see all columns →

Popup

ElementCSS Class
Dialog.rfwc-pro-wheel-popup
Inner wrapper.rfwc-pro-wheel-popup-inner
Close button.rfwc-pro-wheel-popup-close
Scroll to see all columns →

Files Reference

FilePurpose
class-rfwc-pro-lucky-wheel.phpCore wheel class — product mode, rendering, AJAX spin, prize generation
class-rfwc-pro-lucky-wheel-cpt.phpStandalone wheel CPT registration, admin columns
class-rfwc-pro-lucky-wheel-metaboxes.phpEdit screen meta boxes (slices, display rules, coupon, limits, shortcode)
class-rfwc-pro-lucky-wheel-placements.phpAuto-display on cart/checkout/thank-you (block + classic), popup rendering
class-rfwc-pro-lucky-wheel-email-gate.phpEmail collection, custom table, AJAX, coupon emailing, admin page
class-rfwc-pro-settings.phpPro global settings (WooCommerce → Settings → Raffle → Pro)
lucky-wheel.jsFrontend canvas drawing, animation, spin AJAX, email gate JS
admin-lucky-wheel.jsAdmin slice repeater, source toggle, prize type toggle
lucky-wheel-popup.jsPopup trigger logic (delay, scroll, exit intent), localStorage cooldown
lucky-wheel.cssFrontend wheel + email gate styles
lucky-wheel-popup.cssPopup dialog styles
admin.cssAdmin meta box styles
Scroll to see all columns →

Action & Filter Hooks

HookTypeParametersDescription
rfwc_pro_wheel_email_collectedAction$email, $wheel_id, $row_id, $consentFires after email is collected (email gate)
rfwc_pro_wheel_spin_completedAction$wheel_id, $prize_data, $session_emailFires after a standalone wheel spin completes
Scroll to see all columns →